ReactiveCocoa源码解读(二)
上一篇解讀了ReactiveCocoa的三個重要的類的底層實現,本篇繼續。
一、RACMulticastConnection
1.應用
RACMulticastConnection: 用于當一個信號被多次訂閱時,為了保證創建信號時,避免多次調用創建信號的block造成副作用,可以使用該類處理,保證創建信號的block執行一次。
// 創建信號 RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {NSLog(@"發送請求");[subscriber sendNext:@1];return nil; }];// 創建連接 RACMulticastConnection *connect = [signal publish];// 訂閱連接的信號 [connect.signal subscribeNext:^(id x) {NSLog(@"connect 第一次訂閱信號: %@", x); }];[connect.signal subscribeNext:^(id x) {NSLog(@"connect 第二次訂閱信號: %@", x); }];// 連接 [connect connect];2.源碼實現
- 底層原理
- 創建信號
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
// RACDynamicSignal.m + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {RACDynamicSignal *signal = [[self alloc] init];//將代碼塊保存到信號里面(但此時僅僅是保存,沒有調用),所以信號還是冷信號signal->_didSubscribe = [didSubscribe copy];return [signal setNameWithFormat:@"+createSignal:"]; }- 創建連接
[signal publish]
// RACSignal+Operations.m - (RACMulticastConnection *)publish {// 創建訂閱者RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];// 創建connection,參數是剛才創建的訂閱者RACMulticastConnection *connection = [self multicast:subject];return connection; }- (RACMulticastConnection *)multicast:(RACSubject *)subject {[subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];return connection; }// RACMulticastConnection.m - (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {NSCParameterAssert(source != nil);NSCParameterAssert(subject != nil);self = [super init];if (self == nil) return nil;// 保存原始信號_sourceSignal = source;_serialDisposable = [[RACSerialDisposable alloc] init];// 保存訂閱者,即_signal是RACSubject對象_signal = subject;return self; }- 訂閱信號
(RACDisposable *)subscribeNext:(void (^ )(id x))nextBlock;
// RACSignal.m - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {NSCParameterAssert(nextBlock != NULL);RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];return [self subscribe:o]; }// RACSubscriber.m + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {RACSubscriber *subscriber = [[self alloc] init];subscriber->_next = [next copy];subscriber->_error = [error copy];subscriber->_completed = [completed copy];return subscriber; }// RACSubject.m - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {NSCParameterAssert(subscriber != nil);RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];NSMutableArray *subscribers = self.subscribers;@synchronized (subscribers) {[subscribers addObject:subscriber];}return [RACDisposable disposableWithBlock:^{@synchronized (subscribers) {// Since newer subscribers are generally shorter-lived, search// starting from the end of the list.NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {return obj == subscriber;}];if (index != NSNotFound) [subscribers removeObjectAtIndex:index];}}]; }- 連接信號
[connect connect];
// RACMulticastConnection.m - (RACDisposable *)connect {BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);if (shouldConnect) {// 訂閱原生信號self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];}return self.serialDisposable; }// RACDynamicSignal.m - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {NSCParameterAssert(subscriber != nil);RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];if (self.didSubscribe != NULL) {RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{RACDisposable *innerDisposable = self.didSubscribe(subscriber);[disposable addDisposable:innerDisposable];}];[disposable addDisposable:schedulingDisposable];}return disposable; }// RACSubject.m - (void)sendNext:(id)value {// 遍歷_subscribers數組,執行nextBlock[self enumerateSubscribersUsingBlock:^(id subscriber) {[subscriber sendNext:value];}]; }3.流程圖
4.總結
RACMulticastConnection利用RACSubject實現了創建信號的block只執行一次的功能。對于需要對此訂閱信號,但是不希望多次創建信號的應用場合,可以RACMulticastConnection解決。
二、RACCommand
1.應用
RACCommand類用來表示動作的執行, 是對動作觸發后的連鎖事件的封裝。常用在封裝網絡請求,按鈕點擊等等場合。
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {return [RACSignal createSignal:^RACDisposable *(id subscriber) {if (/* DISABLES CODE */ (YES)) {// 正常發送數據,必須發送完成信號[subscriber sendNext:@"Smile"];[subscriber sendCompleted];} else {// 發送錯誤信號[subscriber sendError:[NSError errorWithDomain:@"Network failed" code:0005 userInfo:nil]];}// 信號被銷毀前,做一些清理的工作;如果不需要,可以 return nilreturn [RACDisposable disposableWithBlock:^{NSLog(@"信號被銷毀了");}];}]; }];// 執行信號并訂閱 [[command execute:nil] subscribeNext:^(id x) {NSLog(@"receive data: %@", x); }];2.源碼實現
RACCommand底層實現
1. 創建命令,保存signalBlock 2. 執行命令 * 2.1 調用signalBlock * 2.2 創建connect,傳入RACReplaySubject對象,然后連接信號 3. 訂閱信號 * 3.1 創建訂閱者,保存到RACReplaySubject對象的_subscribers數組中 * 3.2 遍歷valuesReceived數組,調用訂閱者發送數據- 創建command
- (id)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
// RACCommand.m - (id)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock {return [self initWithEnabled:nil signalBlock:signalBlock]; }- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock {NSCParameterAssert(signalBlock != nil);self = [super init];if (self == nil) return nil;_activeExecutionSignals = [[NSMutableArray alloc] init];// 保存創建信號的block_signalBlock = [signalBlock copy];...... }- 執行command
- (RACSignal *)execute:(id)input
// RACCommand.m - (RACSignal *)execute:(id)input {// `immediateEnabled` is guaranteed to send a value upon subscription, so// -first is acceptable here.BOOL enabled = [[self.immediateEnabled first] boolValue];if (!enabled) {NSError *error = [NSError errorWithDomain:RACCommandErrorDomain code:RACCommandErrorNotEnabled userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"The command is disabled and cannot be executed", nil),RACUnderlyingCommandErrorKey: self}];return [RACSignal error:error];}RACSignal *signal = self.signalBlock(input);......// 創建連接,用RACReplaySubject作為訂閱者RACMulticastConnection *connection = [[signalsubscribeOn:RACScheduler.mainThreadScheduler]multicast:[RACReplaySubject subject]];......// 連接信號[connection connect];return [connection.signal setNameWithFormat:@"%@ -execute: %@", self, [input rac_description]]; }// RACMulticastConnection.m - (RACDisposable *)connect {BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);if (shouldConnect) {// 執行創建信號的blockself.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];}return self.serialDisposable; }- 訂閱command
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// RACSignal.m - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {NSCParameterAssert(nextBlock != NULL);RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];return [self subscribe:o]; }// RACReplaySubject.m - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{@synchronized (self) {for (id value in self.valuesReceived) {if (compoundDisposable.disposed) return;// 調用訂閱者,發送數據 "Smile"[subscriber sendNext:(value == RACTupleNil.tupleNil ? nil : value)];}if (compoundDisposable.disposed) return;if (self.hasCompleted) {[subscriber sendCompleted];} else if (self.hasError) {[subscriber sendError:self.error];} else {// 調用父類方法,保存訂閱者到_subscribers數組RACDisposable *subscriptionDisposable = [super subscribe:subscriber];[compoundDisposable addDisposable:subscriptionDisposable];}}}];[compoundDisposable addDisposable:schedulingDisposable];return compoundDisposable; }3.流程圖
4.總結
RACCommand用來封裝事件時,還可以訂閱信號(executionSignals)、訂閱最新信號(switchToLatest)、跳過幾次信號(skip)或信號是否正在執行(executing),在執行信號時,還可以監聽錯誤信號和完成信號,請參考demo例子。
ReactiveCocoa框架的源碼分析暫告一段落,如有分析不足之處,歡迎互相交流。
Demo地址:
RACDemo
轉載于:https://www.cnblogs.com/fishbay/p/7206768.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的ReactiveCocoa源码解读(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux自动挂载
- 下一篇: MongoDB在windows服务器安装