项目中有个小需求,文本框与按钮绑定.当文本框内容符合规则的时候,按钮才会可用.把判定条件修改一下,代码如下:
RAC(self.loginButton,enabled) = [self.textFiled.rac_textSignal map:^id(NSString *value) {
return @(value.length>3);
}];
但是如果在发送请求之后,通过代码清除了文本框的内容,按钮并不会改变状态.
想到了, 应该是这个 rac_textSignal
出现问题了.
看一下它的实现:
- (RACSignal *)rac_textSignal {
@weakify(self);
return [[[[[RACSignal
defer:^{
@strongify(self);
return [RACSignal return:self];
}]
concat:[self rac_signalForControlEvents:UIControlEventAllEditingEvents]]
map:^(UITextField *x) {
return x.text;
}]
takeUntil:self.rac_willDeallocSignal]
setNameWithFormat:@"%@ -rac_textSignal", self.rac_description];
}
几个关键词的解释:
defer
: 将代码的创建推迟到信号被订阅
concat
: 连接信号,第一个信号必须发送完成,第二个信号才会被激活
map
: 映射,将信号内容转换
takeUtil
: signalA takeUntil:signalB 当signalB激活之后,停止signalA 的订阅
其实主要的是, 这个 signal
是监听的: UIControlEventAllEditingEvents
. 那么也就是说对setter
方式不会触发信号
RACObserve
是一个常用的宏,我们都知道是监听属性值改变的.
#define RACObserve(TARGET, KEYPATH) \
({ \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
__weak id target_ = (TARGET); \
[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
_Pragma("clang diagnostic pop") \
})
主要代码是:[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self];
然后这个rac_valuesForKeyPath
的实现如下:
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer {
return [[[self
rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer]
map:^(RACTuple *value) {
// -map: because it doesn't require the block trampoline that -reduceEach: uses
return value[0];
}]
setNameWithFormat:@"RACObserve(%@, %@)", self.rac_description, keyPath];
}
主要代码:
rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer]
也就是这个是通过 KVO
实现的. 而 KVO
得实现是通过临时生成一个子类,并重写父类的 setter
方法.这个在官方文档中有说明:
Key-Value Observing Implementation Details
Automatic key-value observing is implemented using a technique called isa-swizzling.
The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.
You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.
最终实现
了解了两者的实现,就可以很容易实现代码:
RAC(self.loginButton,enabled) =[ [RACObserve(self.textFiled, text) merge:self.textFiled.rac_textSignal ] map:^id(NSString *value) {
return @(value.length>3);
}];