三.ReactiveCocoa代码实践之-更多思考
1. RACObserve()宏形参写法的区别
之前写代码考虑过 RACObserve(self.timeLabel , text) 和 RACObserve(self , timeLabel.text) 的区别。 因为这两种方法都是观察self.timeLabel.text的属性,并且都能实现功能。估计是作者原本用的其中一种后来对另一种也提供了支持,究竟有什么区别哪一种写法更好?
点进去看RACObserve的源码 大多都是方法调用,一层一层点进去最后来到这个方法。
- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver block:(void (^)(id, NSDictionary *, BOOL, BOOL))block
这个方法里面把逗号后面的keypath通过“.” 进行分割成了一个数组。 并且得到三个属性
BOOL keyPathHasOneComponent = (keyPathComponents.count == 1);
NSString *keyPathHead = keyPathComponents[0];
NSString *keyPathTail = keyPath.rac_keyPathByDeletingFirstKeyPathComponent;
这里会取到keypatch的头和去掉头的部分,并且会在下面方法内部自己调用自己
// Adds the callback block to the remaining path components on the value. Also
// adds the logic to clean up the callbacks to the firstComponentDisposable.
void (^addObserverToValue)(NSObject *) = ^(NSObject *value) {
RACDisposable *observerDisposable = [value rac_observeKeyPath:keyPathTail options:(options & ~NSKeyValueObservingOptionInitial) observer:weakObserver block:block];
[firstComponentDisposable() addDisposable:observerDisposable];
};
并且把keyPathTail 作为keypatch传进去了,就是递归调用,每一次进来都会切掉第一个元素,直到BOOL keyPathHasOneComponent 这个值等于yes。从这个角度看用RACObserve(self , timeLabel.text) 这种写法会引发递归调用,性能不如RACObserve(self.timeLabel.text)。
更多RAC宏相关知识可见这篇 :http://blog.sunnyxx.com/2014/03/06/rac_1_macros/
2.集合操作
假设现在有一个需求,有一串密码的数组,我们判断密码长度小于6位就是太短,就会系统内部抛出一个消息:XXX密码太短不合格。采用RAC的写法会比常规写法方便,一个过滤一个自定义然后直接返回。
NSArray *pwds = @[@"567887",@"89877",@"789",@"7899000"];
RACSequence *results = [[pwds.rac_sequence
filter:^ BOOL (NSString *pwd) {
return pwd.length < 6;
}]map:^id(NSString *pwd) {
return [[pwd mutableCopy]stringByAppendingString:@"密码太短不合格"];
}];
NSLog(@"%@",results.array);
中间filter方法的block内代码会在下面results.array代码执行时才会执行, 相当于是有了个订阅者才会执行。这一点和RACSignal很像,因为signal 和 sequence 都是streams,他们共享很多相同的方法signal是push驱动的stream,sequence是pull驱动的stream。
如果相从RACSequence对象中取出其他属性时进行操作也可以用如下方法
RACSequence *s = [RACSequence sequenceWithHeadBlock:^id{
return @"自定义操作";
} tailBlock:^RACSequence *{
return [RACSequence new];
}];
NSLog(@"%@",s.head);
NSLog(@"%@",s.tail);
两个block分别会在指定属性被调用时才会执行,注意head就是sequence的第一个元素,而tail是除去第一个元素的剩余所有,所以还是一个sequence。(董铂然博客园)
3.信号实现游戏技能释放
假设现在需要用RAC模拟一个街机里放爆气技能的方法。 按下了指定的按钮顺序下前下前拳就会释放绝招。
首先需要将各个按钮连线,并设置一个信号来监听所有按键单独信号的并集,捕捉到每个按钮的title。
// 把六个按键的信号合并
RACSignal *comboSignal = [[RACSignal merge:@[
[self.topBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
[self.bottomBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
[self.leftBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
[self.rightBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
[self.BBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
[self.ABtn rac_signalForControlEvents:UIControlEventTouchUpInside]]]
map:^id(UIButton *btn) {
return btn.currentTitle;
}];
然后对这个信号源进行b