很多博客都說RAC好用窘茁,但小編發(fā)現(xiàn)真正缺少的是如何學習RAC的文章哑姚。于是決定自己寫一遍關(guān)于學習ReactiveCocoa的文章,本文主要針對如何從零開始學習ReactiveCocoa譬嚣。
如何學習新的框架
- 掌握這個框架常用類,常用方法
- 框架如何設計的,有哪些好的地方
如何導入ReactiveCocoa
Cocoapods是我們的老朋友了方咆。這里我們就使用它票编。
屏幕快照 2017-09-14 上午10.01.24.png
use_frameworks!
platform :ios, “9.0”
target "RAC練習" do
pod 'ReactiveCocoa', '~> 5.0.0-alpha.5'
end
- 這里注意use_frameworks!不可以少褪储,不然會報錯。
ReactiveCocoa常用類:
一慧域、RACSignal
- 能訂閱鲤竹,不能發(fā)送
- 只能有一個訂閱者
- 多次訂閱,則訂閱者會多次發(fā)送信息
- 只能發(fā)送單個值
- 注意:先訂閱,再發(fā)送
- RACSubscriber訂閱者辛藻,具備發(fā)送消息能力碘橘。
- RACDisposable訂閱者被銷毀時候調(diào)用
// 創(chuàng)建信號
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@2];
// 如果不再發(fā)送數(shù)據(jù),最好發(fā)送信號完成吱肌,內(nèi)部會自動調(diào)用[RACDisposable disposable]取消訂閱信號
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
// 當訂閱者被銷毀的時候就會執(zhí)行
// 信號發(fā)送完成或者發(fā)送錯誤痘拆,也會執(zhí)行block,取消訂閱信號
NSLog(@"信號被銷毀");
}];
}];
// 訂閱信號傳的值
// 底層:創(chuàng)建訂閱者
// 注意:不能分開訂閱氮墨,要一起訂閱
[signal subscribeNext:^(id _Nullable x) {
} error:^(NSError * _Nullable error) {
} completed:^{
}];
二纺蛆、RACSubject
- 可訂閱,可發(fā)送
- 可有多個訂閱者
- 多次訂閱规揪,則訂閱者只發(fā)送信息一次
- 只能發(fā)送單個值
- 注意:先訂閱桥氏,再發(fā)送
// RACSubject語法:
// 創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
// 訂閱
// 內(nèi)部創(chuàng)建RACSubscriber,并且保存起來
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"第一個訂閱者%@",x);
}];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"第二個訂閱者%@",x);
}];
// 發(fā)送信號
// 遍歷所有的訂閱者猛铅,執(zhí)行nextBlock
[subject sendNext:@1];
- RACSubject使用場景:一個數(shù)據(jù)需要多個類同時處理-替代代理:
// 類SUNView中代碼:
@interface SUNView : UIView
@property (nonatomic, strong) RACSubject *subject;
@end
#import "SUNView.h"
@implementation SUNView
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent: (UIEvent *)event{
[_subject sendNext:self];
}
@end
// ViewController中代碼:
SUNView *v = [[SUNView alloc] init];
RACSubject *subject = [RACSubject subject];
// 訂閱
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"點擊了紅色%@",x);
}];
v.subject = subject;
[self.view addSubview:v];
三字支、RACReplaySubject
- 可訂閱,可發(fā)送
- 只能有一個訂閱者
- 可發(fā)送多個值
- 注意:無所謂先訂閱或先發(fā)送
// 創(chuàng)建信號
RACReplaySubject *replaySubject = [RACReplaySubject subject];
// 發(fā)送信息
[replaySubject sendNext:@"123"];
[replaySubject sendNext:@"321"];
// 訂閱信號
// 遍歷值奸忽,讓一個訂閱者去發(fā)送多個值
// 只要訂閱一次祥款,之前所有發(fā)送的值都能獲取到.
[replaySubject subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
四、RACSequence:RAC中的集合(數(shù)組)
- 訂閱集合(獲取數(shù)據(jù))之后月杉,block內(nèi)的操作刃跛,都是在子線程
4.1.OC數(shù)組轉(zhuǎn)化為RACSequence,獲取信號苛萎,訂閱桨昙。( x 為數(shù)組中的單個元素)
NSArray *datas = [NSArray arrayWithContentsOfFile:filePath];
NSMutableArray *arrM = [NSMutableArray array];
// OC數(shù)組轉(zhuǎn)化為RAC數(shù)組,然后獲取信號腌歉,然后訂閱信號(如果數(shù)組中包含的是字典那么蛙酪,返回的x即為字典,免去遍歷數(shù)組的代碼)
[datas.rac_sequence.signal subscribeNext:^(id _Nullable x) {
FlagItem *item = [FlagItem itemWithDict:x];
[arrM addObject:item];
} completed:^{
}];
4.2.OC字典轉(zhuǎn)化為RACSequence翘盖,獲取信號桂塞,訂閱。( x 為單個元組)
NSDictionary *dict = @{@"name" : @"wangsicong", @"money": @100000000};
// 如果是字典那么集合會返回元組RACTuple
[dict.rac_sequence.signal subscribeNext:^(id x) {
//把元祖解析出來
RACTupleUnpack(NSString *key, id value) = x;
}];
//把值包裝成元祖
RACTuple *tuple = RACTuplePack(@1, @2, @3);
4.3.OC數(shù)組轉(zhuǎn)化為RACSequence馍驯,調(diào)用map阁危,array函數(shù)。( x 為數(shù)組中的單個元素)
//使用map函數(shù)汰瘫,直接獲取到字典狂打,然后調(diào)用RAC的array方法,直接返回數(shù)組混弥,比上面代碼更加簡潔
arrM = [[datas.rac_sequence map:^id _Nullable(id _Nullable value) {
return [FlagItem itemWithDict:value];
}] array];
五趴乡、RACMulticastConnection(多路傳送連接)
- 為解決 RACSignal被多次訂閱,訂閱者多次發(fā)送信息,而生晾捏!
- RAC的弱引用:
@weakify(self)
- RAC的強引用:
@strongify(self)
@weakify(self)
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
[self loadData:^(id data) {
[subscriber sendNext:data];
}];
return nil;
}];
//RACSignal轉(zhuǎn)化為RACMulticastConnection蒿涎,RACMulticastConnection對象中有signal
RACMulticastConnection *connection = [signal publish];
[connection.signal subscribeNext:^(id x) {
}];
[connection.signal subscribeNext:^(id x) {
}];
[connection connect];
六、RACCommand
6.1.RACCommand語法
- RACCommand對象內(nèi)部必須返回信號
// 創(chuàng)建Command
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
// RACCommand的block
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"你好"];
[subscriber sendCompleted];
return nil;
}];
}];
- execute 觸發(fā)上面RACCommand的block
// input參數(shù)是@1惦辛;會返回一個信號
[command execute:@1];
6.2.訂閱RACCommand信號
- 用
execute
獲取RACCommand對象返回的信號
[[command execute:@1] subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
- 用
switchToLatest
獲取RACCommand對象返回的信號
// switchToLatest: 獲取最近發(fā)送的信號
[command.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
-
executionSignals
是信號中的信號劳秋,不是對象返回的信號
[command.executionSignals subscribeNext:^(id _Nullable x) {
[x subscribeNext:^(id _Nullable x) {
}];
}];
6.3屬性 executing
監(jiān)聽命令是否完成,第一次用skip跳過
[[command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
BOOL isExecuting = [x boolValue];
if (isExecuting) {
NSLog(@"正在執(zhí)行");
} else {
NSLog(@"執(zhí)行完成");
}
}];
6.4.RACCommand使用場景:_loginButton.rac_command
第一種Button使用方式:
_loginButton.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:input];
return nil;
}];
}];
// 按鈕點擊監(jiān)聽
[_loginButton.rac_command.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
//x即為input
NSLog(@"%@",x);
}];
!第二種Button使用方式:
RACSubject *enableSignal = [RACSubject subject];
_loginButton.rac_command = [[RACCommand alloc] initWithEnabled:enableSignal signalBlock:^RACSignal * _Nonnull(id _Nullable input) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:input];
[subscriber sendCompleted];
return nil;
}];
}];
[[_loginButton.rac_command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
BOOL executing = [x boolValue];
//這個信息直接被按鈕訂閱
[enableSignal sendNext:@(!executing)];
}];