RAC(reactivecocoa)的使用方法


什么是RAC?

幾乎每一篇介紹RAC的文章開頭都是這么一個問題。我這篇文章是寫給新手(包括我自己)看的,所以這個問題更是無法忽視。

簡單的說,RAC就是一個第三方庫,他可以大大簡化你的代碼過程。

官方的說,ReactiveCocoa(其簡稱為RAC)是由GitHub開源的一個應用於iOS和OS X開發的新框架。RAC具有函數式編程響應式編程的特性。

為什么我們要學習RAC?

為了提高我們的開發效率。RAC在某些特定情況下開發時可以大大簡化代碼,並且目前來看安全可靠。

配置RAC環境

我習慣用cocoapods來安裝github上得開源庫,不會的新手iOS開發者有興趣可以去學一下。

想學習cocoapods的同學推薦唐巧前輩的文章

1
2
platform:ios,  '8.0'
pod  'ReactiveCocoa' , '~>2.1.8'

這里有一點要注意下就是RAC的版本問題,由於還沒學習Swift,所以我是用OC編寫程序的,最新版的RAC已經支持Swift了,但是在OC的程序安裝最新版的RAC可能跑不起來,所以推薦大家使用2.5.0版本以下的RAC(具體支持Swift的版本可能有誤,但我引用的2.1.8肯定是沒問題的)。

使用RAC

1.target-action

RAC最基本的入門使用技巧就是對事件的監聽。

PS:在iOS開發中,我們所說的點擊事件其實就是target-action,接觸過iOS開發的人都不會陌生UIControlEventTouchUpInside,這就是按下並松開的動作。不僅僅是UIButton,還有UITextField也有目標-動作模式。

使用前別忘了引用頭文件~

#import <ReactiveCocoa/ReactiveCocoa.h>

接下來就是最關鍵的RAC代碼了。

[[self.textFild rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(id x){ NSLog(@"change"); }];

就這么短短的兩行代碼。他實現了一個功能,即監聽了textFild的UIControlEventEditingChanged事件,當事件發生時實現方法NSLog
所以我們就可以以這段代碼為模板進行RAC的使用,舉一反三,以后的UIButton點擊事件我們都可以用RAC方法進行添加,再也不用add Target了。
對於textFild的文字更改監聽也有更簡單的寫法

[[self.textFild rac_textSignal] subscribeNext:^(id x) { NSLog(@"%@",x); }];

這樣就是每次改變TextFild都輸出改變后的結果。

再比如給我們的某個label添加一個手勢動作,我們也可以用簡單的RAC代碼完成

1
2
3
4
5
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[[tap rac_gestureSignal] subscribeNext:^( id  x) {
     NSLog (@ "tap" );
}];
[ self .view addGestureRecognizer:tap];

這段具體我就不解釋了,相信大家都能看得懂,看不懂的自己寫寫就懂了。

2.代理

用RAC寫代理是有局限的,它只能實現返回值為void的代理方法

首先我們要明白我們為什么要用RAC寫代理?答:簡化代碼!是的,的確為了簡化代碼,為什么我要再這里強調這個,是因為在代理方法中我深深的感受到了RAC的優點。一開始我也不願意花功夫去學RAC,但是我師父給我舉了一個例子,如果一個View里有多個AlertView,每個AlertView有很多個按鈕,每個按鈕都有自己的點擊事件,我應該怎么寫?我想了一下,不但每個按鈕需要打標記,而且每個AlertView也要打標記,然后再往代理點擊事件里加各種方法,代碼就又臭又長。那么讓我們看看RAC怎么寫代理方法。

1
2
3
4
5
6
7
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@ "RAC"  message:@ "RAC TEST"  delegate: self  cancelButtonTitle:@ "cancel"  otherButtonTitles:@ "other" nil ];
[[ self  rac_signalForSelector: @selector (alertView:clickedButtonAtIndex:) fromProtocol: @protocol (UIAlertViewDelegate)] subscribeNext:^(RACTuple *tuple) {
     NSLog (@ "%@" ,tuple.first);
     NSLog (@ "%@" ,tuple.second);
     NSLog (@ "%@" ,tuple.third);
}];
[alertView show];

  

我們來看RAC的語句。@selector是指這次事件監聽的方法fromProtocol指依賴的代理。這里block中有一個RACTuple,他相當於是一個集合類,他下面的first,second等就是類的各個參數,我這里點了AlertView第二個按鈕other輸出了一下。

1
2
3
2016-01-04 18:24:29.114 RACStudyTest[5003:388870] <UIAlertView: 0x7ff260c90c70; frame = (0 0; 0 0); layer = <CALayer: 0x7ff260c91030>>
2016-01-04 18:24:29.115 RACStudyTest[5003:388870] 1
2016-01-04 18:24:29.115 RACStudyTest[5003:388870] (null)

  

可以看出tuple.second是ButtonAtIndex中Button的序號。那么對於上面那個我舉的例子,就可以用switch給各個按鈕添加方法,這樣的代碼看起來更容易理解,方面后期維護。

當然了,AlertView代理也有簡化的代碼。

1
2
3
[[alertView rac_buttonClickedSignal] subscribeNext:^( id  x) {
     NSLog (@ "%@" ,x);
}];

  

這里的x就是各個Button的序號了,可以直接應對我上述遇到的問題。

3.通知

在我們的開發中通知也是一個比較常用的功能,主要的應用場景是某個頁面進行數據重傳需要更新model但是點擊返回棧時不會刷新返回界面的數據,這時就可以用通知來更新另一個頁面的數據,當然我們也可以在另一個頁面的ViewDidAppear方法中刷新數據,但那是題外話。

這里寫的Demo就是我上述說的情況。

首先,在某個頁面中我們需要發出通知,這里就是最基本的通知的寫法。發送名為postdata的通知並傳送一個數組dataArray。

1
2
NSMutableArray  *dataArray = [[ NSMutableArray  alloc] initWithObjects:@ "1" , @ "2" , @ "3" nil ];
[[ NSNotificationCenter  defaultCenter] postNotificationName:@ "postData"  object:dataArray];

  

而在接受的頁面我們需要增加觀察者並接受數組,這時我們的RAC就派上用場了。

1
2
3
4
[[[ NSNotificationCenter  defaultCenter] rac_addObserverForName:@ "postData"  object: nil ] subscribeNext:^( NSNotification  *notification) {
     NSLog (@ "%@" , notification.name);
     NSLog (@ "%@" , notification.object);
}];

  

當這個頁面監聽到名為postdata的通知時他就會執行block中的方法,當然這里的參數改成id x也是可以的,這里用NSNotification主要是強調它的類型。讓我們看看控制台的輸出。

1
2
3
4
5
6
2016-01-04 20:10:52.274 RACStudyTest[5918:439077] postData
2016-01-04 20:10:52.275 RACStudyTest[5918:439077] (
1,
2,
3
)

  

可見,notification.object就是我們想要的數組,當然我們也可以傳一些model。值得一提的是,RAC中的通知不需要remove observer因為在rac_add方法中他已經寫了remove。

4.KVO

RAC中得KVO大部分都是宏定義,所以代碼異常簡潔,簡單來說就是RACObserve(TARGET, KEYPATH)這種形式,TARGET是監聽目標,KEYPATH是要觀察的屬性值,這里舉一個很簡單的例子,如果UIScrollView滾動則輸出success。

1
2
3
4
5
6
7
UIScrollView *scrolView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 200, 400)];
scrolView.contentSize = CGSizeMake(200, 800);
scrolView.backgroundColor = [UIColor greenColor];
[ self .view addSubview:scrolView];
[RACObserve(scrolView, contentOffset) subscribeNext:^( id  x) {
     NSLog (@ "success" );
}];

  

 

 1.遍歷數組
     NSArray *numbers = @[ @1 , @2 , @3 , @4 ];
 
     // 這里其實是三步
     // 第一步: 把數組轉換成集合RACSequence numbers.rac_sequence
     // 第二步: 把集合RACSequence轉換RACSignal信號類,numbers.rac_sequence.signal
     // 第三步: 訂閱信號,激活信號,會自動把集合中的所有值,遍歷出來。
     [numbers.rac_sequence.signal subscribeNext:^( id x) {
 
         NSLog ( @"%@" ,x);
     }];
 
 
     // 2.遍歷字典,遍歷出來的鍵值對會包裝成RACTuple(元組對象)
     NSDictionary *dict = @{ @"name" : @"xmg" , @"age" : @18 };
     [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
 
         // 解包元組,會把元組的值,按順序給參數里面的變量賦值
         RACTupleUnpack( NSString *key, NSString *value) = x;
 
         // 相當於以下寫法
//        NSString *key = x[0];
//        NSString *value = x[1];
 
         NSLog ( @"%@ %@" ,key,value);
 
     }];
 
 
     // 3.字典轉模型
     // 3.1 OC寫法
     NSString *filePath = [[ NSBundle mainBundle] pathForResource: @"flags.plist" ofType: nil ];
 
     NSArray *dictArr = [ NSArray arrayWithContentsOfFile:filePath];
 
     NSMutableArray *items = [ NSMutableArray array];
 
     for ( NSDictionary *dict in dictArr) {
         FlagItem *item = [FlagItem flagWithDict:dict];
         [items addObject:item];
     }
 
     // 3.2 RAC寫法
     NSString *filePath = [[ NSBundle mainBundle] pathForResource: @"flags.plist" ofType: nil ];
 
     NSArray *dictArr = [ NSArray arrayWithContentsOfFile:filePath];
 
     NSMutableArray *flags = [ NSMutableArray array];
 
     _flags = flags;
 
     // rac_sequence注意點:調用subscribeNext,並不會馬上執行nextBlock,而是會等一會。
     [dictArr.rac_sequence.signal subscribeNext:^( id x) {
         // 運用RAC遍歷字典,x:字典
 
         FlagItem *item = [FlagItem flagWithDict:x];
 
         [flags addObject:item];
 
     }];
 
     NSLog ( @"%@" NSStringFromCGRect ([UIScreen mainScreen].bounds));
 
 
     // 3.3 RAC高級寫法:
     NSString *filePath = [[ NSBundle mainBundle] pathForResource: @"flags.plist" ofType: nil ];
 
     NSArray *dictArr = [ NSArray arrayWithContentsOfFile:filePath];
     // map:映射的意思,目的:把原始值value映射成一個新值
     // array: 把集合轉換成數組
     // 底層實現:當信號被訂閱,會遍歷集合中的原始值,映射成新值,並且保存到新的數組里。
     NSArray *flags = [[dictArr.rac_sequence map:^ id ( id value) {
 
         return [FlagItem flagWithDict:value];
 
     }] array];

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM