ReactiveCocoa学习
看了好些天了,老是迷迷瞪瞪的,写下来,比较,分析一下。
这是一些参考网站:基本都是从这些文章上摘录来的。。
http://blog.163.com/l1_jun/blog/static/1438638820142610349839/
http://www.cocoachina.com/applenews/devnews/2014/0115/7702.html
http://blog.segmentfault.com/erliu/1190000000408492
首先:是一些概念。
reactiveCocoa,是用来统一处理响应的一个框架。网上看来的就是:ative app有很大一部分的时间是在等待事件发生,然后响应事件,比如等待网络请求完成,等待用户的操作,等待某些状态值的改变等等,等这些事件发生后,再做进一步处理。 但是这些等待和响应,并没有一个统一的处理方式。Delegate,Notification,Block,KVO,常常会不知道该用哪个最合适。有时需要chain或者compose某几个事件,就需要多个状态变量,而状态变量一多,复杂度也就上来了。为了解决这些问题,Github的工程师们开发了ReactiveCocoa。
ReactiveCocoa试图解决什么问题 传统iOS开发过程中,状态以及状态之间依赖过多的问题 传统MVC架构的问题:Controller比较复杂,可测试性差 提供统一的消息传递机制
RACSignal对象捕捉当前和未来的值。信号可以被观察者链接,组合和反应。信号实际上不会执行,直到它被订阅。
RACObserve使用了KVO来监听property的变化,只要观察的值被自己或外部改变,block就会被执行。但不是所有的property都可以被RACObserve,该property必须支持KVO,比如NSURLCache的currentDiskUsage就不能被RACObserve。KVO现在还不了解。MARK一下。。回头看。
[RACObserve(self,self.testLabel.text) subscribeNext:^(id x) { NSLog(@"%@",@"testlabel"); }];
监听self.testlabel.text,如果text有变化,就执行nslog方法。
代码意思就是观察self.testLabel.text....
// [RACAble(self.testLabel.text) subscribeNext:^(id x) { // NSLog(@"%@",@"testLable"); // }]; 作用和下面这个一样,不知道区别。。但是这个有个警告。
//把testString,self.testLabel.text观察合并起来,其中有一个值发生变化,就调用reduce中的方法。return的值会传到subscribeNext中。也就是说:number *x的值是SSSS。
次方法就是signal的的合并。
[[RACSignal combineLatest: @[RACObserve(self,testString),RACObserve(self,self.testLabel.text)] reduce:^id(NSString *string,NSString *fieldString){ NSLog(@"str: %@,field: %@",string,fieldString); return @"SSSS"; }] subscribeNext:^(NSNumber *x) { NSLog(@"X Class: %@",x); }];
//下面这个是对上面的一个运用。返回的值会被付值给loginbutton.enabel。
RAC(self.logInButton,enabled) = [RACSignal combineLatest:@[ self.usernameTextField.rac_textSignal, self.passwordTextField.rac_textSignal, RACObserve(LoginManager.sharedManager,loggingIn), RACObserve(self,loggedIn) ] reduce:^(NSString *username,NSString *password,NSNumber *loggingIn,NSNumber *loggedIn) { return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue); }];
UIButton *btn1 =[UIButton buttonWithType:UIButtonTypeInfoDark]; [btn1 addTarget:self action:@selector(btnClick1:) forControlEvents:UIControlEventTouchUpInside]; [btn1 setFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/2-10,[UIScreen mainScreen].bounds.size.height/2+20,20,20)];
//按钮被点击的时候会调用下面方法,执行在btnClick1:之后。 btn.rac_command =[[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { NSLog(@"按钮被点击,signal"); return [RACSignal empty]; //为毛返回RACSignal empty啊,有什么用。。。要不是返回空,怎么处理?求教。。
}];
// Notification [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidChangeFrameNotification object:nil] subscribeNext:^(id x) { NSLog(@"键盘Frame改变"); } ];
signal创建完了,如何获取信号,如何处理信号?
//这是网上看的。
冷信号(Cold)和热信号(Hot) 上面提到过这两个概念,冷信号默认什么也不干,比如下面这段代码 RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { NSLog(@"triggered"); [subscriber sendNext:@"foobar"]; [subscriber sendCompleted]; return nil; }]; 我们创建了一个Signal,但因为没有被subscribe,所以什么也不会发生。加了下面这段代码后,signal就处于Hot的状态了,block里的代码就会被执行。 [signal subscribeCompleted:^{ NSLog(@"subscription %u",subscriptions); }];
//创建信号的另一种方式
RACSignal *abc =[@"A B,C D" componentsSeparatedByString:@","].rac_sequence.signal; //componentsSeparatedByString:@",“按@”,“对字符进行分割
[abc subscribeNext:^(NSString *x) { NSLog(@"abc:%@",x); }];
UIView Categories 常用的UIView也都相应的category,比如UIAlertView,就不需要再用Delegate了。
UIAlertView *alertView =[[UIAlertView alloc]initWithTitle:@"" message:@"" delegate:nil cancelButtonTitle:@"A" otherButtonTitles:@"B",@"C",nil]; [[alertView rac_buttonClickedSignal] subscribeNext:^(id x) { NSLog(@"%@",x); }]; [alertView show];
网上的:再说一下UITableViewCell,RAC给UITableViewCell提供了一个方法:rac_prepareForReuseSignal,它的作用是当Cell即将要被重用时,告诉Cell。想象Cell上有多个button,Cell在初始化时给每个button都addTarget:action:forControlEvents,被重用时需要先移除这些target,下面这段代码就可以很方便地解决这个问题:
[[[self.cancelButton rac_signalForControlEvents:UIControlEventTouchUpInside] takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(UIButton *x) { // do other things }];
//这个不是很理解。。。。。
NSObject+RACLifting.h 有时我们希望满足一定条件时,自动触发某个方法,有了这个category就可以这么办 - (void)test { RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,(int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime,dispatch_get_main_queue(),^(void){ [subscriber sendNext:@"A"]; }); return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"B"]; [subscriber sendNext:@"Another B"]; [subscriber sendCompleted]; return nil; }]; [self rac_liftSelector:@selector(doA:withB:) withSignals:signalA,signalB,nil]; } - (void)doA:(NSString *)A withB:(NSString *)B { NSLog(@"A:%@ and B:%@",A,B); }
这里的rac_liftSelector:withSignals 就是干这件事的,它的意思是当signalA和signalB都至少sendNext过一次,接下来只要其中任意一个signal有了新的内容,doA:withB这个方法就会自动被触发
都至少sendnext一次什么意思。。signal怎么有新内容。。原谅我的逗比。。
NSObject+RACSelectorSignal.h 这个category有rac_signalForSelector:和rac_signalForSelector:fromProtocol: 这两个方法。先来看前一个,它的意思是当某个selector被调用时,再执行一段指定的代码,相当于hook。比如点击某个按钮后,记个日志。后者表示该selector实现了某个协议,所以可以用它来实现Delegate。
这个试了几个不知道怎么用。。。
[[self rac_signalForSelector:@selector(viewDidAppear:)] subscribeNext:^(id x) { NSLog(@"viewDidAppear方法执行完毕,这个是挂钩"); }];
[[btn rac_signalForSelector:@selector(setFrame:)] subscribeNext:^(id x) { NSLog(@"btn设置Frame"); }];
这种倒是可以。。
UIButton *btn =[UIButton buttonWithType:UIButtonTypeInfoLight]; [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; [btn setFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/2-10,[UIScreen mainScreen].bounds.size.height/2-10,20)]; [self.view addSubview:btn]; [[btn rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) { NSLog(@"btnClick挂钩"); }];
这种就逗比了。。。不能和button添加的按钮事件挂钩么。。。
//创建自己的RACSignal。但是怎么用啊。。。
-(RACSignal *)urlResults { return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSError *error; NSString *result = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.devtang.com"] encoding:NSUTF8StringEncoding error:&error]; NSLog(@"download"); if (!result) { [subscriber sendError:error]; } else { [subscriber sendNext:result]; [subscriber sendCompleted]; } return [RACDisposable disposableWithBlock:^{ //返回的不是Signal么,这disposable是什么玩意。。。 NSLog(@"clean up"); }]; }]; }
Mapping -map:方法用来改变流中的值并用结果创建一个新的流: RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; // Contains: AA BB CC DD EE FF GG HH II RACSequence *mapped = [letters map:^(NSString *value) { return [value stringByAppendingString:value]; }];
Filtering filter:方法用一个block来判断(test)每一个值,如果判断通过则把这个值加入到结果的流(resulting stream)中: RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; // Contains: 2 4 6 8 RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) { return (value.intValue % 2) == 0; }];
Concatenating -concat:方法将一个流中的值加到另一个中: RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; // Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9 RACSequence *concatenated = [letters concat:numbers];
Combining latest values +combineLatest:以及+combineLatest:reduce:方法会观察(watch)多个信号的变化,然后在一个变化发生的时候向那些信号发送最新的值。 RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSignal *combined = [RACSignal combineLatest:@[ letters,numbers ] reduce:^(NSString *letter,NSString *number) { return [letter stringByAppendingString:number]; }]; // Outputs: B1 B2 C2 C3 [combined subscribeNext:^(id x) { NSLog(@"%@",x); }]; [letters sendNext:@"A"]; [letters sendNext:@"B"]; [numbers sendNext:@"1"]; [numbers sendNext:@"2"]; [letters sendNext:@"C"]; [numbers sendNext:@"3"]; 注意:组合的信号(combined signal)只会在所有的输入至少都有一个值的时候才会发送它的第一个值,比如上面代码中@"A"没有被输出因为numbers还没有收到一个值。
RAC在应用中大量使用了block,由于Objective-C语言的内存管理是基于引用计数 的,为了避免循环引用问题,在block中如果要引用self,需要使用@weakify(self)和@strongify(self)来避免强引用。另外,在使用时应该注意block的嵌套层数,不恰当的滥用多层嵌套block可能给程序的可维护性带来灾难。 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|