簡介
ReactiveCocoa為函數(shù)響應(yīng)式編程(Functional reactive programming,簡稱FRP)轨香,致力于更好得管理事件流和減少不必要的屬性,對于強(qiáng)調(diào)UI響應(yīng)的組件和異步操作(比如網(wǎng)絡(luò)請求)效果尤佳。
關(guān)于RAC不便于調(diào)試的痛點(diǎn),可以借助RAC的instruments插件或者打印信號來降低膏燃。
說了這么多,我們來以對UITextField的信號訂閱為例何什,來分析一下RAC的實(shí)現(xiàn)流程组哩。
信號綁定
RAC是通過Category對UITextField進(jì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];
}
逐步分析如上操作
通過defer
進(jìn)行信號的創(chuàng)建
該創(chuàng)建方式和createSignal
的區(qū)別在于前者是延遲創(chuàng)建,只有在信號被訂閱時才會創(chuàng)建
concat
關(guān)聯(lián)信號
關(guān)聯(lián)信號的實(shí)質(zhì)就是訂閱另一個信號.即點(diǎn)擊事件所觸發(fā)的信號.
[self rac_signalForControlEvents:UIControlEventAllEditingEvents]
.
所有繼承UIControl
的類都可以對操作事件進(jìn)行訂閱,事件訂閱是如何實(shí)現(xiàn)的呢?
我們來看一下代碼:
- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {
@weakify(self);
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
@strongify(self);
[self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
[self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
[subscriber sendCompleted];
}]];
return [RACDisposable disposableWithBlock:^{
@strongify(self);
[self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
}];
}]
setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", self.rac_description, (unsigned long)controlEvents];
}
關(guān)鍵代碼: [self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
當(dāng)前信號訂閱了信號B,信號B在所有UITextField
的編輯狀UIControlEventAllEditingEvents
事件被觸發(fā)時,信號B發(fā)送next
事件,進(jìn)而通知當(dāng)前信號
map
修改返回值
map
方法的作用,即修改信號內(nèi)的返回值,由于外部并不關(guān)心UITextField
本身,而是它的文本內(nèi)容,所以只返回text
屬性.
map
是怎么實(shí)現(xiàn)的呢?
- (instancetype)map:(id (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^(id value) {
return [class return:block(value)];
}] setNameWithFormat:@"[%@] -map:", self.name];
}
在這里我們討論一下map
和flattenMap
的使用場景
-
map
用于處理信號的返回值 -
flattenMap
用于處理信號中的信號
takeUntil
確定信號的生命周期
訂閱UITextField
的生命周期信號,在組件dealloc
的時候,處理掉該信號
看代碼
- (RACSignal *)rac_willDeallocSignal {
RACSignal *signal = objc_getAssociatedObject(self, _cmd);
if (signal != nil) return signal;
RACReplaySubject *subject = [RACReplaySubject subject];
[self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
[subject sendCompleted];
}]];
objc_setAssociatedObject(self, _cmd, subject, OBJC_ASSOCIATION_RETAIN);
return subject;
}
RAC通過runtime
對dealloc
信號進(jìn)行管理,addDisposable
的官方解釋是Adds the given disposable. If the receiving disposable has already been disposed of, the given disposable is disposed immediately
,即可以理解為是兩個信號關(guān)聯(lián)釋放.在這里的作用是,當(dāng)外部訂閱生命周期的信號被釋放時,監(jiān)聽生命周期的信號本身已經(jīng)沒有存在的意義,隨之釋放.
setNameWithFormat
設(shè)置信號名
為信號設(shè)置一個別名,用于后續(xù)的調(diào)試,這里就不做贅述了.
總結(jié)
通過對 UITextField
的分析,我們可以大致了解RAC內(nèi)部的管理流程和RAC的精髓signal
的使用方法.希望有助于大家對RAC的理解.