ReactiveCocoa操作
所有的信號(RACSignal)都可以進行操作處理汽纤,因為所有的操作方法都定義在RACStream.h中艾帐,而RACSignal繼承RACStream威根。
ReactiveCocoa操作思想
運用的是(鉤子)HooK思想纬傲,Hook是一種用于改變API執(zhí)行結(jié)果的技術(shù)。
Hook用處:截獲API調(diào)用的技術(shù)。
Hook原理:在每次調(diào)用一個API返回結(jié)果之前,先執(zhí)行自己的方法,改變方法的輸出晶丘。
RAC開發(fā)方式:RAC核心開發(fā)方式也就是綁定,之前的開發(fā)方式是賦值唐含,而RAC開發(fā)應該把重心放在綁定浅浮,也就是在創(chuàng)建一個對象的時候就綁定好以后想要做的事情,而不是等賦值之后在去做事情捷枯。
例如:把數(shù)據(jù)展示在控件上滚秩,之前都是重寫控件的setModel方法,用RAC就可以在一開始創(chuàng)建控件的時候就綁定好數(shù)據(jù)
ReactiveCocoa核心方法bind
ReactiveCocoa操作的核心方法是bind(綁定)淮捆,給RAC中的信號進行綁定郁油,只要信號一發(fā)送數(shù)據(jù)就能監(jiān)聽到,從而把發(fā)送數(shù)據(jù)改成自己想要的數(shù)據(jù)攀痊。
在開發(fā)中很少使用bind方法桐腌,bind屬于RAC中的底層方法,RAC已經(jīng)封裝了很多好用的其他方法苟径,底層都是調(diào)用bind,用法比bind簡單案站。
bind方法簡單介紹和使用:
// 創(chuàng)建信號
- RACSubject *subject = [RACSubject subject];
// 綁定信號,只有綁定的信號被訂閱就會被調(diào)用
// bind 返回一個綁定信號 - RACSignal *bindsignal = [subject bind:^RACStreamBindBlock{
return ^RACSignal *(id value, BOOL *stop){
// block調(diào)用:只要原信號發(fā)送數(shù)據(jù),就會調(diào)用block
// block作用:處理原信號內(nèi)容
// value:原信號發(fā)送的內(nèi)容
NSLog(@"接收到原信號的內(nèi)容%@",value);
// 返回信號,不能傳nil,返回空信號[RACSignal empty]
value = @"程倩";
return [RACReturnSignal return:value];
};
}];
- [bindsignal subscribeNext:^(id x) {
NSLog(@"接收到綁定信號處理完的內(nèi)容%@",x);
}];
// 原型號發(fā)送數(shù)據(jù) - [subject sendNext:@"123"];
輸出:
2016-05-17 11:41:00.549 ReactiveCocoaTest1[1102:67222] 接收到原信號的內(nèi)容123
2016-05-17 11:41:00.549 ReactiveCocoaTest1[1102:67222] 接收到綁定信號處理完的內(nèi)容程倩
分析
-
RACSubject *subject = [RACSubject subject];
創(chuàng)建一個RACSubject原信號 初始化一個數(shù)組_subscribers
源碼:
@implementation RACSubject#pragma mark Lifecycle
- (instancetype)subject {
return [[self alloc] init];
}
-
(id)init {
self = [super init];
if (self == nil) return nil;
//初始化一個訂閱者和_subscribers數(shù)組
_disposable = [RACCompoundDisposable compoundDisposable];
_subscribers = [[NSMutableArray alloc] initWithCapacity:1];return self;
}
- (instancetype)subject {
RACSignal *bindsignal = [subject bind:^RACStreamBindBlock{
return ^RACSignal (id value, BOOL stop){
NSLog(@"接收到原信號的內(nèi)容%@",value);
// 返回信號,不能傳nil,返回空信號[RACSignal empty]
value = @"程倩";
return [RACReturnSignal return:value];
};
}];
bind:這個函數(shù)存在于RACSubject的父類RACSignal中
源碼:
/
- @param block 傳入的block
- @return 返回綁定后的信號
*/
- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
NSCParameterAssert(block != NULL);// 一個異常處理棘街,不太懂
// 直接返回了一個綁定后的信號 創(chuàng)建一個新的信號返回蟆盐,創(chuàng)建的是(RACDynamicSignal類型信號)
// RACDynamicSignal是RACSignal的子類 將這個didSubscribe block塊保存在了RACDynamicSignal中的_didSubscribe block中
{
可以看到 [RACSignal createSignal:]中返回的結(jié)果是[RACDynamicSignal createSignal:didSubscribe];
看一下[RACDynamicSignal createSignal:didSubscribe]中的源碼:
@implementation RACDynamicSignal
- (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
返回的是RACDynamicSignal對象,并且將傳入的block塊保存在了對象的_didSubscribe中
}
// 所以說這個block現(xiàn)在并沒有被執(zhí)行遭殉,只是將這個block存在了綁定信號中了
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
//當綁定的信號被訂閱的時候開始執(zhí)行這個block
//調(diào)用block后得到創(chuàng)建綁定信號時傳入block返回的block 這個block返回一個新的信號
RACStreamBindBlock bindingBlock = block();
NSMutableArray *signals = [NSMutableArray arrayWithObject:self];
RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
// 這個block暫時不會被執(zhí)行
void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) {
BOOL removeDisposable = NO;
@synchronized (signals) {
[signals removeObject:signal];
if (signals.count == 0) {
[subscriber sendCompleted];
[compoundDisposable dispose];
} else {
removeDisposable = YES;
}
}
if (removeDisposable) [compoundDisposable removeDisposable:finishedDisposable];
};
// 這個block暫時不會被執(zhí)行
void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
@synchronized (signals) {
[signals addObject:signal];
}
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
// 在這里對返回的RACReturnSignal內(nèi)容處理完成的信號進行訂閱
RACDisposable *disposable = [signal subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(signal, selfDisposable);
}
}];
selfDisposable.disposable = disposable;
};
@autoreleasepool {
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
//當代碼執(zhí)行到這里的時候
// 在這里對原信號進行訂閱石挂,當原信號發(fā)送數(shù)據(jù)的時候會調(diào)用這個Nextblock
RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
if (compoundDisposable.disposed) return;
BOOL stop = NO;
// 當原信號發(fā)送數(shù)據(jù)的時候在這里調(diào)用了之前綁定block返回的block內(nèi)容,得到一個信號
id signal = bindingBlock(x, &stop);
@autoreleasepool {
// 這這里執(zhí)行了addSignal block,傳入綁定block執(zhí)行完畢返回的信號
if (signal != nil) addSignal(signal);
if (signal == nil || stop) {
[selfDisposable dispose];
completeSignal(self, selfDisposable);
}
}
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(self, selfDisposable);
}
}];
selfDisposable.disposable = bindingDisposable;
}
return compoundDisposable;
}] setNameWithFormat:@"[%@] -bind:", self.name];
}
3.訂閱綁定信號:
[bindsignal subscribeNext:^(id x) {
NSLog(@"接收到綁定信號處理完的內(nèi)容%@",x);
}];
分析:
subscribeNext:函數(shù)源碼
bindsignal的類型是RACDynamicSignal類型
(RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
[self subscribe:o]的源碼是:-
(RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
// 這個函數(shù)里面記錄了綁定信號的訂閱者
源碼{
- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
NSCParameterAssert(subscriber != nil);self = [super init];
if (self == nil) return nil;// 記錄下了綁定信號的訂閱者
_innerSubscriber = subscriber;
_signal = signal;
_disposable = disposable;[self.innerSubscriber didSubscribeWithDisposable:self.disposable];
return self;
}
pragma mark RACSubscriber
-
(void)sendNext:(id)value {
if (self.disposable.disposed) return;if (RACSIGNAL_NEXT_ENABLED()) {
RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
}
// 在這里拿到綁定后的信號訂閱者發(fā)送數(shù)據(jù)
[self.innerSubscriber sendNext:value];
}}
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
// 在這里可以看到訂閱綁定信號的時候調(diào)用了創(chuàng)建綁定信號時候傳入的block 跳轉(zhuǎn)到2步驟中的 block
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable];
}];
[disposable addDisposable:schedulingDisposable];
}
return disposable;
}
- // 原型號發(fā)送數(shù)據(jù)
[subject sendNext:@"123"];
分析:
拿到數(shù)組中的所有的block調(diào)用恩沽,(原信號被訂閱時候存下的block)
例子:
假設想監(jiān)聽文本框的內(nèi)容誊稚,并且在每次輸出結(jié)果的時候翔始,都在文本框的內(nèi)容拼接一段文字“輸出:”
// 方式一:在返回結(jié)果后罗心,拼接。
[_textfield.rac_textSignal subscribeNext:^(id x) {
NSLog(@"輸出:%@",x);
}];
// 方式二:在返回結(jié)果前城瞎,拼接渤闷,使用RAC中bind方法做處理。
// bind方法參數(shù):需要傳入一個返回值是RACStreamBindBlock的block參數(shù)
// RACStreamBindBlock是一個block的類型脖镀,返回值是信號飒箭,參數(shù)(value,stop),因此參數(shù)的block返回值也是一個block。
// RACStreamBindBlock:
// 參數(shù)一(value):表示接收到信號的原始值弦蹂,還沒做處理
// 參數(shù)二(*stop):用來控制綁定Block肩碟,如果*stop = yes,那么就會結(jié)束綁定。
// 返回值:信號凸椿,做好處理削祈,在通過這個信號返回出去,一般使用RACReturnSignal,需要手動導入頭文件RACReturnSignal.h脑漫。
// bind方法使用步驟:
// 1.傳入一個返回值RACStreamBindBlock的block髓抑。
// 2.描述一個RACStreamBindBlock類型的bindBlock作為block的返回值。
// 3.描述一個返回結(jié)果的信號优幸,作為bindBlock的返回值吨拍。
// 注意:在bindBlock中做信號結(jié)果的處理。
// 底層實現(xiàn):
// 1.源信號調(diào)用bind,會重新創(chuàng)建一個綁定信號网杆。
// 2.當綁定信號被訂閱羹饰,就會調(diào)用綁定信號中的didSubscribe,生成一個bindingBlock碳却。
// 3.當源信號有內(nèi)容發(fā)出严里,就會把內(nèi)容傳遞到bindingBlock處理,調(diào)用bindingBlock(value,stop)
// 4.調(diào)用bindingBlock(value,stop)追城,會返回一個內(nèi)容處理完成的信號(RACReturnSignal)刹碾。
// 5.訂閱RACReturnSignal,就會拿到綁定信號的訂閱者座柱,把處理完成的信號內(nèi)容發(fā)送出來迷帜。
// 注意:不同訂閱者,保存不同的nextBlock色洞,看源碼的時候戏锹,一定要看清楚訂閱者是哪個。
// 這里需要手動導入#import <ReactiveCocoa/RACReturnSignal.h>火诸,才能使用RACReturnSignal锦针。
[[_textfield.rac_textSignal bind:^RACStreamBindBlock{
// 什么時候調(diào)用:
// block作用:表示綁定了一個信號.
return ^RACStream *(id value, BOOL *stop){
// 什么時候調(diào)用block:當信號有新的值發(fā)出,就會來到這個block置蜀。
// block作用:做返回值的處理
// 做好處理奈搜,通過信號返回出去.
return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]];
};
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
不正確之處,歡迎補充
測試代碼 https://github.com/CharType/ReactiveCocoaTest