您的位置:首页 > Web前端 > React

ReactiveCocoa学习

2015-06-29 10:44 756 查看
看了好些天了,老是迷迷瞪瞪的,写下来,比较,分析一下。

这是一些参考网站:基本都是从这些文章上摘录来的。。
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, 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可能给程序的可维护性带来灾难。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: