一.Getting started
專案中使用ReactiveCocoa
在這裡筆者使用Cocoapods
安裝ReactiveCocoa
,在專案中建立podfile
檔案,使用的是2.5
版本.
platform :ios, '8.0'
#use_frameworks!
target '你的專案名稱’ do
pod 'ReactiveCocoa', '~> 2.5’
end
複製程式碼
執行安裝命令
pod install --no-repo-update
複製程式碼
二.ReactiveCocoa常見類
1.RACSignal
訊號類
RACSignal
訊號類表示當資料改變時,在訊號內部會利用訂閱者傳送資料.RACSignal
預設是一個冷訊號
,只有被訂閱以後才會變成熱訊號.
RACSignal
訊號類的簡單使用:
// 1.建立訊號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.利用訂閱者傳送資料
// 只有當有訂閱者訂閱時,才會呼叫這個block
[subscriber sendNext:@"這是傳送的資料"];
return nil;
}];
// 2.訂閱訊號
[signal subscribeNext:^(id x) {
NSLog(@"接收到資料:%@",x);
}];
複製程式碼
2.RACSubscriber
訂閱者
RACSubscriber
是一個協議,任何遵循RACSubscriber
協議的物件並且實現協議方法都可以是一個訂閱者,訂閱者可以幫助訊號傳送資料.
RACSubscriber
協議中有四個方法.
- (void)sendNext:(id)value;
- (void)sendError:(NSError *)error;
- (void)sendCompleted;
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
複製程式碼
3.RACDisposable
RACDisposable
用於取消訂閱和清理資源,當訊號傳送完成或傳送錯誤時會自動呼叫.
// 1.建立訊號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.利用訂閱者傳送資料
[subscriber sendNext:@"這是傳送的資料"];
// 如果為未呼叫,當訊號傳送完成或傳送錯誤時會自動呼叫
return [RACDisposable disposableWithBlock:^{
NSLog(@"資源被清理了");
}];
}];
// 2.訂閱訊號
[signal subscribeNext:^(id x) {
NSLog(@"接收到資料:%@",x);
}];
複製程式碼
4.RACSubject
訊號提供者
RACSubject
繼承RACSignal
,又遵循了RACSubscriber
協議,所以既可以充當訊號,又可以傳送訊號,通常用它代替代理.
// 1.建立訊號
RACSubject *subject = [RACSubject subject];
// 2.訂閱訊號
[subject subscribeNext:^(id x) {
NSLog(@"接收到資料:%@",x);
}];
// 3.傳送訊號
[subject sendNext:@"傳送資料"];
複製程式碼
RACSubject
的底層實現
- 在執行
[RACSubject subject]
時,RACSubject
會在初始化時建立disposable
物件屬性和subscribers
訂閱者陣列.
- 在執行
subscribeNext
訂閱訊號時,會建立一個訂閱者RACSubscriber
,並將訂閱者RACSubscriber
新增到subscribers
訂閱者陣列.
- 在執行
sendNext
傳送訊號時,會遍歷subscribers
訂閱者陣列,執行sendNext
5.RACReplaySubject
RACReplaySubject
重複提供訊號類,RACSubject
的子類.由於RACReplaySubject
的底層實現和RACSubject
不同,RACReplaySubject
可以先傳送資料,再訂閱訊號.
// 1.建立訊號
RACReplaySubject *replaySubject = [RACReplaySubject subject];
// 2.訂閱訊號
[replaySubject subscribeNext:^(id x) {
NSLog(@"訂閱訊號,%@",x);
}];
// 3.傳送資料
[replaySubject sendNext:@"傳送的資料"];
複製程式碼
RACReplaySubject
的底層實現
- 在執行
[RACReplaySubject subject]
時,建立一個valuesReceived
陣列
- 在執行
subscribeNext
時,建立訂閱者,遍歷valuesReceived陣列,利用訂閱者執行sendNext
傳送valuesReceived
中的資料.
- 在執行
sendNext
時,將要傳送的資料儲存到valuesReceived陣列中,執行sendNext
6.RACMulticastConnection
我們在使用RACsignal
,RACReplaySubject
或者RACReplaySubject
時,當一個訊號被多個訂閱者訂閱時,在訊號內部的block
或被呼叫多次,有時這樣並不能滿足我們的需求,我們想要訊號被多個訂閱者訂閱時,訊號內部的block
只被執行一次,那麼RACMulticastConnection
就能幫助我們完成需求.
// 1.建立訊號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.傳送資料
NSLog(@"傳送資料");
[subscriber sendNext:@"傳送資料"];
return nil;
}];
RACMulticastConnection *connection = [signal publish];
// 2.訂閱訊號
[connection.signal subscribeNext:^(id x) {
NSLog(@"接收到資料1:%@",x);
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"接收到資料2:%@",x);
}];
// 4.啟用訊號
[connection connect];
複製程式碼
在log
中的列印如下
2017-06-20 16:55:50.809 MVVMRACDemoOC[2848:856666] 傳送資料
2017-06-20 16:55:50.810 MVVMRACDemoOC[2848:856666] 接收到資料1:傳送資料
2017-06-20 16:55:50.810 MVVMRACDemoOC[2848:856666] 接收到資料2:傳送資料
複製程式碼
7.RACCommand
RACCommand
是處理事件的類,可以把事件如何處理,事件中的資料如何傳遞,包裝到這個類中.
使用一個demo
說明RACCommand
:監聽按鈕的點選,傳送網路請求.
// 1.建立命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
NSLog(@"接收到命令:%@", input);
// 返回一個訊號,不能為空.(訊號中的訊號)
// 3.建立訊號用來傳遞資料
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"訊號中的訊號傳送的資料"];
// 注意:資料傳遞完成,要呼叫sendCompleted才能執行完畢
[subscriber sendCompleted];
return nil;
}];
}];
self.command = command;
// 2.訂閱訊號中的訊號(必須要在執行命令前訂閱)
[command.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"接收到訊號中的訊號傳送的資料:%@",x);
}];
// 4.執行命令
[command execute:@1];
[[command.executing skip:1] subscribeNext:^(id x) {
if ([x boolValue] == YES) {
NSLog(@"正在執行");
}else{
NSLog(@"未開始/執行完成");
}
}];
複製程式碼
在log
中的列印
2017-06-20 17:26:28.013 MVVMRACDemoOC[3166:970238] 接收到命令:1
2017-06-20 17:26:28.016 MVVMRACDemoOC[3166:970238] 正在執行
2017-06-20 17:26:28.016 MVVMRACDemoOC[3166:970238] 接收到訊號中的訊號傳送的資料:訊號中的訊號傳送的資料
2017-06-20 17:26:28.017 MVVMRACDemoOC[3166:970238] 未開始/執行完成
複製程式碼
三.常見巨集
RAC(TARGET, [KEYPATH, [NIL_VALUE]])
給某個物件的某個屬性做繫結.
// 只要passwordTextField內容變化,accountTextField的text就會跟著改變
RAC(self.accountTextField, text) = self.passwordTextField.rac_textSignal;
複製程式碼
RACObserve(self, name)
監聽某個物件的某個屬性,返回訊號
// 監聽passwordTextField背景色的改變
[RACObserve(self.passwordTextField, backgroundColor) subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
複製程式碼
@weakify(Obj)
和@strongify(Obj)
一般用來防止迴圈引用,組合使用.RACTuplePack
把資料包裝成RACTuple(元組類)
// 把引數中的資料包裝成元組
RACTuple *tuple = RACTuplePack(@1,@2);
複製程式碼
RACTupleUnpack
把RACTuple(元組類)解包成對應的資料
// 把引數中的資料包裝成元組
RACTuple *tuple = RACTuplePack(@"OneAlon",@"HangZhou");
// 解包元組,會把元組的值,按順序給引數裡面的變數賦值
RACTupleUnpack(NSString *name,NSNumber *address) = tuple;
複製程式碼
四.常見用法
- 代替代理
代替代理有兩種方法,一種是使用
RACSubject
代替代理,另一種是使用rac_signalForSelector
方法代替代理. 這裡模擬一個需求:自定義一個紅色的view,在view中有一個按鈕,監聽按鈕的點選.
如果不使用RAC
,在紅色的view
中定義一個代理屬性,點選按鈕的時候通知代理做事情. 如果使用RAC
,直接讓紅色的view
呼叫rac_signalForSelector
方法即可.
[[self.redView rac_signalForSelector:@selector(buttonClick:)] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
複製程式碼
2.代替KVO
監聽紅色view
的背景色的改變
[[self.redView rac_valuesAndChangesForKeyPath:@"backgroundColor" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
複製程式碼
3.監聽事件
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"按鈕被點選了");
}];
複製程式碼
4.代替通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"鍵盤彈出");
}];
複製程式碼
5.監聽文字框文字改變
[_textField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"文字改變了%@",x);
}];
複製程式碼
6.處理當介面有多次請求時,需要都獲取到資料時,才能展示介面,rac_liftSelector:withSignalsFromArray:Signals
五.Demo
做了一個小demo
,僅供學習使用.
六.參考文獻
Objc中國 MVVM 介紹 ReactiveCocoa 和 MVVM 入門 最快讓你上手ReactiveCocoa之基礎篇 最快讓你上手ReactiveCocoa之進階篇 學習視訊