項目中有個小需求,文本框與按鈕綁定.當文本框內(nèi)容符合規(guī)則的時候,按鈕才會可用.把判定條件修改一下,代碼如下:
RAC(self.loginButton,enabled) = [self.textFiled.rac_textSignal map:^id(NSString *value) {
return @(value.length>3);
}];
但是如果在發(fā)送請求之后,通過代碼清除了文本框的內(nèi)容,按鈕并不會改變狀態(tài).
想到了, 應(yīng)該是這個 rac_textSignal
出現(xiàn)問題了.
看一下它的實現(xiàn):
- (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];
}
幾個關(guān)鍵詞的解釋:
defer
: 將代碼的創(chuàng)建推遲到信號被訂閱
concat
: 連接信號,第一個信號必須發(fā)送完成,第二個信號才會被激活
map
: 映射,將信號內(nèi)容轉(zhuǎn)換
takeUtil
: signalA takeUntil:signalB 當signalB激活之后,停止signalA 的訂閱
其實主要的是, 這個 signal
是監(jiān)聽的: UIControlEventAllEditingEvents
. 那么也就是說對setter
方式不會觸發(fā)信號
RACObserve
是一個常用的宏,我們都知道是監(jiān)聽屬性值改變的.
#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
的實現(xiàn)如下:
- (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
實現(xiàn)的. 而 KVO
得實現(xiàn)是通過臨時生成一個子類,并重寫父類的 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.
最終實現(xiàn)
了解了兩者的實現(xiàn),就可以很容易實現(xiàn)代碼:
RAC(self.loginButton,enabled) =[ [RACObserve(self.textFiled, text) merge:self.textFiled.rac_textSignal ] map:^id(NSString *value) {
return @(value.length>3);
}];