ReactiveCocoa源碼與坑

在閱讀源碼之前容我拋出個小問題,看看下面的代碼??

//發送驗證碼
-(void)sendCodeRequesSignal{
   RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [self.accountServiceAssembly.authService sendResetPWDOTPCoder:self.phone withSuccessCallBack:^(id data) {
            [subscriber sendNext:data];
            [subscriber sendCompleted];
        } withFailedCallBack:^(NSError *error) {
            error = [HttpErrorCodeHandler errorWithCode:error.code userinfo:nil];
            [subscriber sendError:error];
        }];
        return nil;
    }];
    [signal subscribeNext:^(id data) {
        NSLog(@"%@", data);
    }];
}

這是公司項目中對RAC的一段使用,生成的RACSignal實例自始至終都沒有被引用,而這里是有一個異步請求的。作為一個熱信號,請求結束后signal是應該已經釋放掉了,又是如何做出響應的???(聰明的你可能已經猜到了與subscriber有關??)。

好了,上面的問題先放一下。本文不會詳細的講述ReactiveCocoa各API的使用,著重說說一下幾點 :
1、RACSignal源碼
2、RACCommond源碼
3、bind方法
4、踩坑

<b>一、RACSignal</b>
下面是RACSignal最基本的一個使用??

 RACSignal *signal = [RACDynamicSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        //1、發送信號
        [subscriber sendNext:@"窈窕淑女"];

        // //理論上這里可以對整個信息通道做任意修改,直接屬性調用nextBlock發送信號
        RACPassthroughSubscriber *passSubscriber = (RACPassthroughSubscriber *)subscriber;
        RACSubscribeNextBlock nextBlock = [[passSubscriber valueForKeyPath:@"innerSubscriber"] valueForKeyPath:@"next"];
        nextBlock(@"直接屬性調用");
       
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"清理工作");
        }];
    }];
    //第一次訂閱信號
    RACDisposable *disposeable1 = [signal subscribeNext:^(id x1) {
        NSLog(@"君子好逑1");
    }];
    //第二次訂閱信號
    RACDisposable *disposeable2 = [signal subscribeNext:^(id x2) {
        NSLog(@"君子好逑2");
    }];

    //生成靜態(block不執行)信息->訂購(觸發信號)->訂購事件做出響應

這里初始化了一個信號,并做了兩次訂閱。先看下信號是怎么生成的, RACSignal有一個RACDynamicSignal的子類只有create和subscribe兩個接口

+ (RACSignal *)createSignal:(RACDidSubscribeBlock)didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

這里只生成了一個signal對象,并存儲了入參didSubscribe block,從字面量不難猜測是在訂閱之后執行的。
接著我們來看下subscribeNext:的實現

- (RACDisposable *)subscribeNext:(RACSubscribeNextBlock)nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    //傳入nextBlock后生成一個記錄此任務的訂購者(RACSubscriber);
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    //生成的"訂購者"又如何與信號(self)關聯上呢?-> RACDynamicSignal
    return [self subscribe:o];
}
+ (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;
}

從訂閱者RACSubscriber的初始化api可知,一個訂閱者是可以包含next、error、complete。而且如下兩種實現方式并不是等效的,雖然效果可能是一樣的(后面會講到):

//訂閱方法1
    [signal subscribeNext:^(id x) {
    }];
    [signal subscribeError:^(NSError *error) {
    }];
    [signal subscribeCompleted:^{
    }];
    //訂閱方法2
    [signal subscribeNext:^(id x) {
    } error:^(NSError *error) {
    } completed:^{
    }];

這里我們已經生成了一個完整的訂購者和信號,但兩者又是如何關聯起來的呢,繼續看最后一句的實現,RACDynamicSignal中最重要的一個接口

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    //相當于init
    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
 //1
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
    //此時的subscriber對象包含了"信號源(signal)"和"訂購者"的全部信息,形成一個完整的信息通道對象

    if (self.didSubscribe != NULL) {
        RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{

            //關鍵:在這里觸發了didSubscribe任務塊,并傳入一個"包含了全量信息"的"訂購者"。
            RACDisposable *innerDisposable = self.didSubscribe(subscriber);

            [disposable addDisposable:innerDisposable];
        }];
        //這里注冊的任務是直接執行的,schedulingDisposable為全局屬性做一些清理工作。

        [disposable addDisposable:schedulingDisposable];
    }
    
    return disposable;
}

我們先看第一步,這里生成了一個RACPassthroughSubscriber類型的對象,我們看其入參subscriber、self、disposable包含了信號、訂閱者的全部信息。再看self.didSubscribe(subscriber)這句,signal存儲的block在此時才執行,并傳入一個持有了全量信息的的subscriber。
這里我們可以回頭看下篇首的那個問題,在ReactiveCocoa 的實現過程中RACSignal并沒有持有Subscriber, 而是生成的RACPassthroughSubscriber持有了這兩者。這樣一來只要subscriber沒被釋放,異步請求返回后RACSignal自然就不會被釋放了。我們隨便打個斷點看看就能知道了??


屏幕快照 2017-02-21 下午4.26.54.png

到這里我們的信號終于觸發了,但怎樣告知訂閱者呢?從上圖innerSubscriber的4個屬性可以很輕松地看出來。直接調用subscriber的三個接口就是了。理論上通過subscriber你可以對整個信號通道的任意屬性進行修改,但那是不符合游戲規則的??

- (void)sendNext:(id)value {
    @synchronized (self) {
        void (^nextBlock)(id) = [self.next copy];
        if (nextBlock == nil) return;

        nextBlock(value);
    }
}
- (void)sendError:(NSError *)e {
    @synchronized (self) {
        void (^errorBlock)(NSError *) = [self.error copy];
        [self.disposable dispose];

        if (errorBlock == nil) return;
        errorBlock(e);
    }
}
- (void)sendCompleted {
    @synchronized (self) {
        void (^completedBlock)(void) = [self.completed copy];
        [self.disposable dispose];

        if (completedBlock == nil) return;
        completedBlock();
    }
}

上面這三個訂閱方法,沒什么好說的了。需要注意下sendNext是沒有調用dispose方法的,所以信號發送完了不要忘記sendCompleted方法的調用哦。也正因如此我們可以多次調用sendNext:哦。
通過源碼的分析,我們很容易看出RACSignal是個不折不扣的熱信號。當然,我們有木有辦法不這樣用呢,當然可以。篇首的問題就是拋出的磚,我們拿到subscriber不立即發消息出去,先存起來,等想要發出信號的時候再通知訂閱者。事實上我們的項目中有不少這樣的用法。如果覺得這樣用很詭異,那我們接著往下看真正的冷信號RACCommand??

<b>二、RACCommand</b>
因為RACCommand中有對RACSubject原理的應用,所以先簡單的說下RACSubject,不廢話,就摳兩段代碼看看你們就懂了

    NSMutableArray *subscribers = self.subscribers;
    @synchronized (subscribers) {
        [subscribers addObject:subscriber];
    }
- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
    NSArray *subscribers;
    @synchronized (self.subscribers) {
        subscribers = [self.subscribers copy];
    }

    for (id<RACSubscriber> subscriber in subscribers) {
        block(subscriber);
    }
}

#pragma mark RACSubscriber

- (void)sendNext:(id)value {
    [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
        [subscriber sendNext:value];
    }];
}

和上面的RACSignal一個最明顯的區別是:RACSignal每次訂閱都會立即得到反饋(簡單點說每訂閱一次會有一個RACPassthroughSubscriber生成),是一一對應的。而RACSubject會收集多個訂閱者(RACPassthroughSubscriber)后,一次信息的發送多個訂閱者響應。


img

看上圖,RACCommand其實就比RACSubject多了個Worker而已。接下來先看個command簡單的使用

一樣的先上個簡單的??

//1
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:input];
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
            }];
        }];
    }];
    //executionSignals: RACSignal
//2
    [command.executionSignals subscribeNext:^(RACSignal *signal) {
        NSLog(@"訂閱的信號執行"); //x 是signal
    }];
//3
    [command.executionSignals.switchToLatest subscribeNext:^(id x) {
        NSLog(@"%@", x); //x 是data
    }];
//4、開始執行命令
    [command execute:@"全軍撤退"];
    [command execute:@"回防高地"];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [command execute:@"發起進攻"];
    });

相對來說command就要復雜很多了,第一步我們創建了一個command命令,第二步和第三步又有什么區別,為什么會有這樣的區別呢?待會再說。第四步我們的command能這樣連續發送執行命令么?好了,暈已。還是慢慢看吧。

- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock {
    NSCParameterAssert(signalBlock != nil);

    self = [super init];
    if (self == nil) return nil;

    //1、創建儲存管理的容器(signal生成block)
    _activeExecutionSignals = [[NSMutableArray alloc] init];
    _signalBlock = [signalBlock copy];

    //2、監聽容器中block元素的改變。
    RACSignal *newActiveExecutionSignals = [[[[[self
        rac_valuesAndChangesForKeyPath:@keypath(self.activeExecutionSignals) options:NSKeyValueObservingOptionNew observer:nil]
        reduceEach:^(id _, NSDictionary *change) {
            NSArray *signals = change[NSKeyValueChangeNewKey];
            if (signals == nil) return [RACSignal empty];

            return [signals.rac_sequence signalWithScheduler:RACScheduler.immediateScheduler];
        }]
        concat]
        publish]
        autoconnect];
    //2、這里還是處理第二步生成的signal
    _executionSignals = [[[newActiveExecutionSignals
        map:^(RACSignal *signal) {
            return [signal catchTo:[RACSignal empty]];
        }]
        deliverOn:RACScheduler.mainThreadScheduler]
        setNameWithFormat:@"%@ -executionSignals", self];
    
    // `errors` needs to be multicasted so that it picks up all
    // `activeExecutionSignals` that are added.
    //
    // In other words, if someone subscribes to `errors` _after_ an execution
    // has started, it should still receive any error from that execution.
    RACMulticastConnection *errorsConnection = [[[newActiveExecutionSignals
        flattenMap:^(RACSignal *signal) {
            return [[signal
                ignoreValues]
                catch:^(NSError *error) {
                    return [RACSignal return:error];
                }];
        }]
        deliverOn:RACScheduler.mainThreadScheduler]
        publish];
    
    _errors = [errorsConnection.signal setNameWithFormat:@"%@ -errors", self];
    [errorsConnection connect];

    RACSignal *immediateExecuting = [RACObserve(self, activeExecutionSignals) map:^(NSArray *activeSignals) {
        return @(activeSignals.count > 0);
    }];

    _executing = [[[[[immediateExecuting
        deliverOn:RACScheduler.mainThreadScheduler]
        // This is useful before the first value arrives on the main thread.
        startWith:@NO]
        distinctUntilChanged]
        replayLast]
        setNameWithFormat:@"%@ -executing", self];

    RACSignal *moreExecutionsAllowed = [RACSignal
        if:RACObserve(self, allowsConcurrentExecution)
        then:[RACSignal return:@YES]
        else:[immediateExecuting not]];
    
    if (enabledSignal == nil) {
        enabledSignal = [RACSignal return:@YES];
    } else {
        enabledSignal = [[[enabledSignal
            startWith:@YES]
            takeUntil:self.rac_willDeallocSignal]
            replayLast];
    }
    
    _immediateEnabled = [[RACSignal
        combineLatest:@[ enabledSignal, moreExecutionsAllowed ]]
        and];
    
    _enabled = [[[[[self.immediateEnabled
        take:1]
        concat:[[self.immediateEnabled skip:1] deliverOn:RACScheduler.mainThreadScheduler]]
        distinctUntilChanged]
        replayLast]
        setNameWithFormat:@"%@ -enabled", self];

    return self;
}

這并不是一段好理解的代碼,需要綁定的東西太多了。你只需要明白這里只做了一件事,生成一個數組,并監聽這個數組的任意改變事件。著重理解下executionSignals,這是一個包含信號的信號。這樣一來,下面這兩句代碼就不難理解了??

[command.executionSignals subscribeNext:^(RACSignal *signal) {
        NSLog(@"訂閱的信號執行"); //x 是signal
    }];
 [command.executionSignals.switchToLatest subscribeNext:^(id x) {
        NSLog(@"%@", x); //x 是data
    }];

前者是command中管理者拋出來的信號,后者才是訂閱了我們最后生成的那個signal。 command中雖然只存了一個生成信號的block:_signalBlock = [signalBlock copy],調用signalBlock生成signal對象便交由executionSignals管理了。好了,signalBlock是什么時候調用的呢?

- (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];
    }
    //1
    RACSignal *signal = self.signalBlock(input);
    NSCAssert(signal != nil, @"nil signal returned from signal block for value: %@", input);

    // We subscribe to the signal on the main thread so that it occurs _after_
    // -addActiveExecutionSignal: completes below.
    //
    // This means that `executing` and `enabled` will send updated values before
    // the signal actually starts performing work.
    RACMulticastConnection *connection = [[signal
        subscribeOn:RACScheduler.mainThreadScheduler]
        multicast:[RACReplaySubject subject]];
    
    @weakify(self);

    [self addActiveExecutionSignal:connection.signal];
    [connection.signal subscribeError:^(NSError *error) {
        @strongify(self);
        [self removeActiveExecutionSignal:connection.signal];
    } completed:^{
        @strongify(self);
        [self removeActiveExecutionSignal:connection.signal];
    }];

    [connection connect];
    return [connection.signal setNameWithFormat:@"%@ -execute: %@", self, [input rac_description]];
}

當我們調用execute后,會生成signal,此時我們給executionSignals(RACSignal)訂閱的事件自然會做出相應。但RACSignal中的didSubscribe并未執行。我們可以通過以下兩種方式訂閱這個包含在信號中的信號

[command.executionSignals subscribeNext:^(RACSignal *signal1) {
        NSLog(@"訂閱的信號執行1___%p", signal1); //signal p = 0x608000025160
        [signal1 subscribeNext:^(id x) {
            NSLog(@"%@", x);
        }];
    }];
    [command.executionSignals.switchToLatest subscribeNext:^(id x) {
        NSLog(@"%@", x); //x = 1
    }];

從源碼中我們只需要看出來RACCommand是一個包含了信號的信號,通過對signal的包裝實現了信息的雙向傳遞。

<b>三、bind方法</b>
前面我們分析了信息的一對一傳遞(RACSignal),一對多傳遞(RACSubject),雙向傳遞(RACCommand)。接下來我們來看看多個信號的的綁定

RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"bindSignal___a"];
        [subscriber sendCompleted];
        return nil;
    }];
    RACSignal *testSignal = [signal1 bind:^RACStreamBindBlock{
        //RACStreamBindBlock (^block)(void) 類型的入參
        
        return ^RACSignal* (id value, BOOL *stop){
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
                [subscriber sendNext:[NSString stringWithFormat:@"%@+bindSignal___b", value]];
                [subscriber sendCompleted];
                return nil;
            }];
        };
    }];
    [testSignal subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];

bind api我們一般不直接使用,但會有很多基于bind所生成的接口。在這段示例代碼中,我們創建了signal1并創建了signal2與signal1綁定,生成testSignal后訂閱該signal。

- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
    NSCParameterAssert(block != NULL);

    /*
     * -bind: should:
     * 
     * 1. Subscribe to the original signal of values.
     * 2. Any time the original signal sends a value, transform it using the binding block.
     * 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received.
     * 4. If the binding block asks the bind to terminate, complete the _original_ signal.
     * 5. When _all_ signals complete, send completed to the subscriber.
     * 
     * If any signal sends an error at any point, send that to the subscriber.
     */

    //1、這里我們只需要知道傳入的block是一個可以生成signal2的塊。返回的是合并后的signal。這里和command對熱信號的封裝很相似。只是command是以對象的形式存儲,這里是以block的形式存儲
    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {

        RACStreamBindBlock bindingBlock = block();

        //2、bindingBlock 是一個執行即能生成一個signal的block
        
        NSMutableArray *signals = [NSMutableArray arrayWithObject:self];

        RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

        //結束處理
        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];
        };
        //添加處理
        void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
            @synchronized (signals) {
                [signals addObject:signal];
            }
            RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
            [compoundDisposable addDisposable:selfDisposable];
            //6、signal2被訂閱
            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];

            //3、訂閱合并后的signal后,開始訂閱第一個signal。
            RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
                // Manually check disposal to handle synchronous errors.

                if (compoundDisposable.disposed) return;
                BOOL stop = NO;

                //4、接收到第一個signal后,調用傳入的block生成第二個signal。
                id signal = bindingBlock(x, &stop);

                @autoreleasepool {
                    //5、到這里還有signal2沒有被訂閱了,只能在addSignal(signal)里了
                    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];
}

我們來看下bind的執行順序,首先調用signal1的bind方法,傳入的并不是signal,而是一個調用即可生成一個signal的block。上面的代碼中直接返回的是合并后的testSignal。當testSignal被訂閱后,上面的這段didSubscribe代碼塊才開始執行,跳過預處理代碼直接看第3步[self subscribeNext:], 這里的self其實是signal1,直接訂閱了signal1,獲取到signal1傳遞來的消息(id x)后,將其當作“signal2生成block”的入參,對應的就是前面示例中的value了。
至此signal2只是生成了,而并未被訂閱。再看第5、6步了,在addSignal 里面直接訂閱了signal2,并通過[subscriber sendNext:x];把消息發送給了testSignal的訂閱者。
這樣一來總結下就成了如下順序了:
生成一個包含[signal1,signal2生成block]處理邏輯的signal->訂閱signal->訂閱signal1->生成signal2->訂閱signal2。
如果多次訂閱testSignal, signal1對象一直是那個signal1,而signal2已不再是那個signal2了。

<b>四、踩坑</b>
1、RACObserver

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { //1
        MTModel *model = [[MTModel alloc] init]; // MTModel有一個名為的title的屬性
        [subscriber sendNext:model];
        [subscriber sendCompleted];
        return nil;
    }];
    self.flattenMapSignal = [signal flattenMap:^RACStream *(MTModel *model) { //2
        return RACObserve(model, title);
    }];
    [self.flattenMapSignal subscribeNext:^(id x) { //3
        NSLog(@"subscribeNext - %@", x);
    }];

這里就不繞圈子了,RACObserver是會造成循環引用的,直接看RACObserver源碼??

 #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") \
    })

好了,找到self就懂,不廢話。

2、sendComplete的調用問題(或者說RACSignal與RACSubject的持有問題)

//不會有任何問題
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"next"];
        return nil;
    }];
    [signal subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
    //會導致subject的不釋放
    RACSubject *subject = [RACSubject subject];
    [subject subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
    [subject sendNext:@"next"];

subscriber(: RACPassthroughSubscriber)持有property:@[signal, subscriber, disposable];
signal僅持有didSubscribe,并沒有持有任何subscriber

subject(:RACSignal)持有訂閱者數組:@[subscriber1(: RACPassthroughSubscriber),
subscriber2(: RACPassthroughSubscriber),
subscriber3(: RACPassthroughSubscriber)]。
而數組中的每一個訂閱者都是持有signal, subscriber, disposable三項的。

所以其實這里的循環引用一直都在,不要說用weak,這里是必須用強引用的。好了,ReactiveCocoa只能自己處理內存問題了(也必須是這樣)。由于sendNext:事件是可以多次觸發的,唯一的解決方案只有用sendComplelte了。如果還不懂的話,可以回頭看前面對RACSignal和RACSubject源碼的分析就通透了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • RAC使用測試Demo下載:github.com/FuWees/WPRACTestDemo 1.ReactiveC...
    FuWees閱讀 6,440評論 3 10
  • 前言 之前對RAC有了一個基本的認識,了解了它的作用,以及RAC的運行機制,我們知道只要是信號(RACSignal...
    大大盆子閱讀 4,512評論 0 11
  • 標簽: iOS RAC 概述 ReactiveCocoa是一個函數響應式編程框架,它能讓我們脫離Cocoa AP...
    GodyZ閱讀 7,570評論 16 97
  • 前言由于時間的問題,暫且只更新這么多了,后續還會持續更新本文《最快讓你上手ReactiveCocoa之進階篇》,目...
    Karos_凱閱讀 1,751評論 0 6
  • 一、他,總是喜歡坐在門外的欄桿上,看著街上的人來人往;他,總是端坐在沙發上,捧著一本書在看;他,總是坐在電腦前,寫...
    愛讀書的benyouyou閱讀 263評論 0 1