如何使用ReactiveCocoa
ReactiveCocoa可以在iOS和OS X的應(yīng)用開發(fā)中使用,對于iOS開發(fā)者,可以將RAC源碼下載編譯后,使用編譯好的libReactiveCocoa-iOS.a文件。
開發(fā)者也可以用CocoaPods來設(shè)置目標(biāo)工程對ReactiveCocoa的依賴,只需要編輯Podfile文件,增加如下內(nèi)容即可:
pod 'ReactiveCocoa', '~> 4.0.2-alpha-1'

一般會(huì)導(dǎo)入不成功
** 報(bào)錯(cuò)提示信息**

需要在podfile加上use_frameworks,重新pod install 才能導(dǎo)入成功(這個(gè)是應(yīng)為框架是swift的緣故)
use_frameworks!

ReactiveCocoa的特點(diǎn)
RAC在應(yīng)用中大量使用了block,由于Objective-C語言的內(nèi)存管理是基于引用計(jì)數(shù) 的,為了避免循環(huán)引用問題,在block中如果要引用self,需要使用@weakify(self)和@strongify(self)來避免強(qiáng)引用。另外,在使用時(shí)應(yīng)該注意block的嵌套層數(shù),不恰當(dāng)?shù)臑E用多層嵌套block可能給程序的可維護(hù)性帶來災(zāi)難。
RAC的編程方式和傳統(tǒng)的MVC方式差異巨大,所以需要較長的學(xué)習(xí)時(shí)間。
對于一個(gè)常用框架的學(xué)習(xí)先要了解常用類和項(xiàng)目的基本結(jié)構(gòu)
ReactiveCocoa框架的結(jié)構(gòu) 圖(圖來自刀哥分享, 刀哥Github)

** RAC中最核心的類RACSiganl**
搞定RACSiganl,這個(gè)類就能用ReactiveCocoa開發(fā)了。
RACSiganl:信號類,一般表示將來有數(shù)據(jù)傳遞,只要有數(shù)據(jù)改變,信號內(nèi)部接收到數(shù)據(jù),就會(huì)馬上發(fā)出數(shù)據(jù)。
先通過一個(gè)簡單的示例來進(jìn)行學(xué)習(xí):

將下面的代碼添加到viewDidLoad方法的最后:
// rac_textSignal 是一個(gè)通過分類實(shí)現(xiàn)的信號。subscribeNext 是訂閱信號。
// ReactiveCocoa框架使用category來為很多基本UIKit控件添加signal。這樣你就能給控件添加訂閱了,text field的rac_textSignal就是這么來的。
[self.usernameTextField.rac_textSignal subscribeNext:^(id x){
NSLog(@"%@", x);
}];
編譯運(yùn)行,在用戶名輸入框中輸幾個(gè)字。注意console的輸出應(yīng)該和下面的類似。

2015-10-01 21:37:04.619 ReactiveCocoaDemo[4212:72362] 1
2015-10-01 21:37:05.967 ReactiveCocoaDemo[4212:72362] 12
2015-10-01 21:37:06.636 ReactiveCocoaDemo[4212:72362] 123
2015-10-01 21:37:07.543 ReactiveCocoaDemo[4212:72362] 1234
2015-10-01 21:37:08.620 ReactiveCocoaDemo[4212:72362] 12345
2015-10-01 21:37:09.079 ReactiveCocoaDemo[4212:72362] 123456
2015-10-01 21:37:09.488 ReactiveCocoaDemo[4212:72362] 1234567
2015-10-01 21:37:10.120 ReactiveCocoaDemo[4212:72362] 12345678
ReactiveCocoa signal(RACSignal)發(fā)送事件流給它的subscriber。目前總共有三種類型的事件:next、error、completed。一個(gè)signal在因error終止或者完成前,可以發(fā)送任意數(shù)量的next事件。在本教程的第一部分,我們將會(huì)關(guān)注next事件。
RACSignal有很多方法可以來訂閱不同的事件類型。每個(gè)方法都需要至少一個(gè)block,當(dāng)事件發(fā)生時(shí)就會(huì)執(zhí)行block中的邏輯。在上面的例子中可以看到每次next事件發(fā)生時(shí),subscribeNext:方法提供的block都會(huì)執(zhí)行。
ReactiveCocoa有很多操作來控制事件流。假設(shè)你只關(guān)心超過3個(gè)字符長度的用戶名,那么你可以使用filter操作來實(shí)現(xiàn)這個(gè)目的。把之前加在viewDidLoad中的代碼更新成下面的:
// filter :過濾器 將信號進(jìn)行過濾,從而得到我們想要的信號。 subscribeNext :訂閱信號。
**只有訂閱了的信號,信號流才會(huì)起作用。**
[[self.usernameTextField.rac_textSignal
filter:^BOOL(id value){
NSString*text = value;
return text.length > 3;
}]
subscribeNext:^(id x){
NSLog(@"%@", x);
}];

// 這個(gè)代碼是不會(huì)起作用的。
[self.usernameTextField.rac_textSignal
filter:^BOOL(id value){
NSString*text = value;
return text.length > 3;
}];
響應(yīng)式編程的本質(zhì)是響應(yīng)鏈條(也就是信號管道),根據(jù)響應(yīng)鏈條來表達(dá)應(yīng)用的功能。

從上面的圖中可以看到,rac_textSignal是起始事件。然后數(shù)據(jù)通過一個(gè)filter,如果這個(gè)事件包含一個(gè)長度超過3的字符串,那么該事件就可以通過。管道的最后一步就是subscribeNext:,block在這里打印出事件的值。
filter操作的輸出也是RACSignal,這點(diǎn)先放到一邊。你可以像下面那樣調(diào)整一下代碼來展示每一步的操作。
// 這個(gè)是將上面的信號管道進(jìn)行拆分
// 1. 先獲取 self.usernameTextField.rac_textSignal 信號
RACSignal *usernameSourceSignal =
self.usernameTextField.rac_textSignal;
// 2. 在過濾信號
RACSignal *filteredUsername =[usernameSourceSignal
filter:^BOOL(id value){
NSString*text = value;
return text.length > 3;
}];
// 3. 訂閱信號
[filteredUsername subscribeNext:^(id x){
NSLog(@"%@", x);
}];
RACSignal的每個(gè)操作都會(huì)返回一個(gè)RACsignal,這在術(shù)語上叫做連貫接口(fluent interface)。這個(gè)功能可以讓你直接構(gòu)建管道,而不用每一步都使用本地變量。
[[[self.usernameTextField.rac_textSignal
// 轉(zhuǎn)換信號 將textSignal 轉(zhuǎn)換為NSMumber
map:^id(NSString*text){
return @(text.length);
}]
// 過濾信號
filter:^BOOL(NSNumber*length){
return[length integerValue] > 3;
}]
// 訂閱信號
subscribeNext:^(id x){
NSLog(@"%@", x);
}];

新加的map操作通過block改變了事件的數(shù)據(jù)。map從上一個(gè)next事件接收數(shù)據(jù),通過執(zhí)行block把返回值傳給下一個(gè)next事件。在上面的代碼中,map以NSString為輸入,取字符串的長度,返回一個(gè)NSNumber。

能看到map操作之后的步驟收到的都是NSNumber實(shí)例。你可以使用map操作來把接收的數(shù)據(jù)轉(zhuǎn)換成想要的類型,只要它是個(gè)對象。
創(chuàng)建一些信號,來表示用戶名和密碼輸入框中的輸入內(nèi)容是否有效
// 信號是否有效并返回信號
RACSignal *validUsernameSignal =
[self.usernameTextField.rac_textSignal
map:^id(NSString *text) {
return @([self isValidUsername:text]);
}];
RACSignal *validPasswordSignal =
[self.passwordTextField.rac_textSignal
map:^id(NSString *text) {
return @([self isValidPassword:text]);
}];
// 將信號進(jìn)行轉(zhuǎn)換 并訂閱信號
[[validUsernameSignal map:^id(NSNumber *usernameValid){
return[usernameValid boolValue] ? [UIColor clearColor]:[UIColor yellowColor];
}] subscribeNext:^(UIColor *color){
self.usernameTextField.backgroundColor = color;
}];
[[validPasswordSignal map:^id(NSNumber *passwordValid){
return[passwordValid boolValue] ? [UIColor clearColor]:[UIColor yellowColor];
}] subscribeNext:^(UIColor *color){
self.passwordTextField.backgroundColor = color;
}];
上例代碼的簡化
RAC(self.passwordTextField, backgroundColor) = [validPasswordSignal map:^id(NSNumber *passwordValid){
return[passwordValid boolValue] ? [UIColor clearColor]:[UIColor yellowColor];
}];
RAC(self.usernameTextField, backgroundColor) = [validUsernameSignal map:^id(NSNumber *passwordValid){
return[passwordValid boolValue] ? [UIColor clearColor]:[UIColor yellowColor];
}];
RAC宏允許直接把信號的輸出應(yīng)用到對象的屬性上。RAC宏有兩個(gè)參數(shù),第一個(gè)是需要設(shè)置屬性值的對象,第二個(gè)是屬性名。每次信號產(chǎn)生一個(gè)next事件,傳遞過來的值都會(huì)應(yīng)用到該屬性上。

- 注意:
- 信號類(RACSiganl),只是表示當(dāng)數(shù)據(jù)改變 時(shí),信號內(nèi)部會(huì)發(fā)出數(shù)據(jù),它本身不具備發(fā)送信號的能力,而是交給內(nèi)部一個(gè)訂閱者去發(fā)出。
- 默認(rèn)一個(gè)信號都是冷信號,也就是值改變了,也不會(huì)觸發(fā),只有訂閱了這個(gè)信號,這個(gè)信號才會(huì)變?yōu)闊嵝盘?,值改變了才?huì)觸發(fā)。
- 如何訂閱信號:調(diào)用信號RACSignal的subscribeNext就能訂閱。
學(xué)習(xí)博客原文推薦:
ReactiveCocoa - iOS開發(fā)的新框架 ——唐巧的技術(shù)博客
最快讓你上手ReactiveCocoa之基礎(chǔ)篇——袁錚
ReactiveCocoa入門教程——第一部分——BenBeng