ios RAC使用總結(jié)(RACSignal、RACDisposable、RACSubject)

在開(kāi)始MVVM之前,RAC是肯定繞不過(guò)去的。MVVM+RAC真的是雙劍合璧。今天 我們先來(lái)看看RAC的使用和一些原理實(shí)現(xiàn),然后再結(jié)合項(xiàng)目中的登錄來(lái)實(shí)踐一下。

RACSignal

先從最簡(jiǎn)單的RACSignal開(kāi)始,我們先來(lái)看看它是怎么創(chuàng)建的。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [self creatSigal];
}

-(void)creatSigal{
    
    RACSignal *sigal=[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        [subscriber sendNext:@"你個(gè)小樣"];
        
        return nil;
    }];
    
    [sigal subscribeNext:^(id  _Nullable x) {
       
        NSLog(@"傳遞的數(shù)據(jù)是:----%@",x);
    }];
    
}

輸出打印的是:LBDaySurgery(Dev)[10211:3806511] 傳遞的數(shù)據(jù)是:----你個(gè)小樣

上面就創(chuàng)建了一個(gè)信號(hào),并且訂閱信號(hào),還發(fā)送消息了。創(chuàng)建是成功了,但是它具體的原來(lái)還不知道啊。不要慌,我們點(diǎn)進(jìn)方法看看。

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

創(chuàng)建信號(hào)的方法是這么個(gè)東西。首先RACDynamicSignal這是個(gè)什么,然后再點(diǎn)發(fā)現(xiàn)

@interface RACDynamicSignal : RACSignal

它其實(shí)就是一個(gè)信號(hào),先初始化了RACDynamicSignal,然后下面_didSubscribe這個(gè)熟悉嘛,是不是有點(diǎn)像訂閱信號(hào),RACDynamicSignal持有了didSubscribe,最后返回了一個(gè)信號(hào)。這個(gè)方法看完了,有一個(gè)疑問(wèn),didSubscribe這個(gè)是用來(lái)干嘛的?接著往下看
訂閱信號(hào)的方法

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}

第一行很容易理解,就是創(chuàng)建RACSubscriber(訂閱者),點(diǎn)進(jìn)去看看它里面的實(shí)現(xiàn)

+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
    RACSubscriber *subscriber = [[self alloc] init];

    subscriber->_next = [![邏輯圖.png](https://upload-images.jianshu.io/upload_images/9788931-865189b71e7896ea.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
 copy];
    subscriber->_error = [error copy];
    subscriber->_completed = [completed copy];

    return subscriber;
}

我們發(fā)現(xiàn),它初始化了訂閱者,然后訂閱者持有了next這個(gè)block,持有了error、completed,很簡(jiǎn)單。點(diǎn)進(jìn)去看[self subscribe:o];的實(shí)現(xiàn)

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

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
//看的有點(diǎn)懵,上面的對(duì)象都不知道是什么 但是didSubscribe這個(gè)熟悉啊,在創(chuàng)建信號(hào)的時(shí)候RACDynamicSignal持有didSubscribe
    if (self.didSubscribe != NULL) {
        RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
//這里調(diào)用didSubscribe方法,并且把剛才傳入的subscriber調(diào)用出去
            RACDisposable *innerDisposable = self.didSubscribe(subscriber);
            [disposable addDisposable:innerDisposable];
        }];

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

現(xiàn)在我們最開(kāi)始的疑問(wèn)解開(kāi)了,didSubscribe的調(diào)用。這個(gè)方法里能看懂的就是,先判斷有沒(méi)有didSubscribe,有的話就執(zhí)行這個(gè)方法,參數(shù)是subscriber訂閱者。后面我們?cè)谡f(shuō)RACDisposable。

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

        nextBlock(value);
    }
}

上面是訂閱者發(fā)送消息的方法實(shí)現(xiàn),判斷有沒(méi)有nextblock,而這里的nextblock也正是subscriber->_next = [next copy];保存的。如果有 就把發(fā)送的消息,傳遞出去。
上面我們一個(gè)一個(gè)的都點(diǎn)進(jìn)去看了他們的具體實(shí)現(xiàn),現(xiàn)在來(lái)張圖更加直觀的看看這個(gè)過(guò)程


邏輯圖.png

這里面有要說(shuō)明的是:didSubscribe的參數(shù)是RACSubscriber,而在我們發(fā)送消息的時(shí)候, [subscriber sendNext:@"你個(gè)小樣"]; 這里的訂閱者就是didSubscribe傳入的,而訂閱者持有nextblock,所以nextBlock(value);消息就傳入到訂閱的block中了。這就是創(chuàng)建RACSignal的一個(gè)流程。

RACDisposable

在上面創(chuàng)建信號(hào)的時(shí)候,返回了空,但是我們點(diǎn)進(jìn)方法里面,它應(yīng)該返回一個(gè)RACDisposable。
1.什么是RACDisposable

RACDisposable:用于取消訂閱或者清理資源,當(dāng)信號(hào)銷(xiāo)毀或者發(fā)送錯(cuò)誤的時(shí)候,就會(huì)自動(dòng)觸發(fā)。
使用場(chǎng)景:不想監(jiān)聽(tīng)某個(gè)信號(hào)時(shí),可以通過(guò)它主動(dòng)取消訂閱信號(hào)
我們來(lái)看看它的使用:

-(void)creatSigal{
    
    RACSignal *sigal=[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        NSLog(@"創(chuàng)建成功");
        [subscriber sendNext:@"你個(gè)小樣"];
        NSLog(@"發(fā)送消息之后");
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"這是什么什么時(shí)候調(diào)用");
        }];
    }];
    [sigal subscribeNext:^(id  _Nullable x) {
        NSLog(@"傳遞的數(shù)據(jù)是:----%@",x);
    }];
    
}
打印出:2021-04-08 14:08:52.687740+0800 LBDaySurgery(Dev)[10407:3878985] 創(chuàng)建成功
2021-04-08 14:08:52.687781+0800 LBDaySurgery(Dev)[10407:3878985] 傳遞的數(shù)據(jù)是:----你個(gè)小樣
2021-04-08 14:08:52.687795+0800 LBDaySurgery(Dev)[10407:3878985] 發(fā)送消息之后
2021-04-08 14:08:52.687819+0800 LBDaySurgery(Dev)[10407:3878985] 這是什么什么時(shí)候調(diào)用

我們發(fā)現(xiàn)信號(hào)被銷(xiāo)毀以后,就就會(huì)調(diào)用。
另外我們還可以主動(dòng)調(diào)用 也會(huì)銷(xiāo)毀

 RACDisposable *disposable=[sigal subscribeNext:^(id  _Nullable x) {
        NSLog(@"傳遞的數(shù)據(jù)是:----%@",x);
    }];
    [disposable dispose];
RACSubject

創(chuàng)建信號(hào)當(dāng)然不是一種方法,還有其他的方法。先上代碼

-(void)creatracSubject{
    
    RACSubject *subject=[RACSubject subject];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"收到的消息是:---%@",x);
    }];
    //發(fā)送消息
    [subject sendNext:@"666 666"];
    
}
 LBDaySurgery(Dev)[10505:3899054] 收到的消息是:---666 666

我們點(diǎn)進(jìn)方法看看它的實(shí)現(xiàn)原理

+ (instancetype)subject {
    return [[self alloc] init];
}
- (instancetype)init {
    self = [super init];
    if (self == nil) return nil;

    _disposable = [RACCompoundDisposable compoundDisposable];
    _subscribers = [[NSMutableArray alloc] initWithCapacity:1];
    
    return self;
}

創(chuàng)建subject對(duì)象時(shí),初始化了一個(gè)取消信號(hào)和一個(gè)數(shù)組,從名字我們能看出這是用來(lái)存放訂閱者的。
接著看訂閱信號(hào)的實(shí)現(xiàn)

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}

好熟悉,和RACSignal一毛一樣,但是里面里面的實(shí)現(xiàn)就不同了,而且他兩也不是同一個(gè)類(lèi)。創(chuàng)建RACSubscriber *o 這個(gè)是一樣的我們不需要看,主要是下面的[self subscribe:o];

- (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];
    }
    
    [disposable addDisposable:[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];
        }
    }]];

    return disposable;
}

從上面這段代碼能看到,是把訂閱者都加入到subscribers數(shù)組中了。

我們?cè)倏窗l(fā)布消息的實(shí)現(xiàn)

- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
    NSArray *subscribers;
    @synchronized (self.subscribers) {
        subscribers = [self.subscribers copy];
    }
//遍歷subscribers中的訂閱者,然后block傳遞subscriber
    for (id<RACSubscriber> subscriber in subscribers) {
        block(subscriber);
    }
}

#pragma mark RACSubscriber

- (void)sendNext:(id)value {
    [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
//這里和之前RACSignal發(fā)送消息相同
        [subscriber sendNext:value];
    }];
}

1.創(chuàng)建的subject內(nèi)部會(huì)創(chuàng)建數(shù)組_subscribers,用來(lái)保存所有的訂閱者。
2.訂閱信息的時(shí)候會(huì)創(chuàng)建訂閱者
3.發(fā)送消息的時(shí)候,會(huì)依次發(fā)送。

-(void)creatracSubject{
    
    RACSubject *subject=[RACSubject subject];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"收到的消息是:---%@",x);
    }];
    //發(fā)送消息
    [subject sendNext:@"666 666"];
    
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"第二次收到:--%@",x);
    }];
    
    [subject sendNext:@"888888"];
    
}

上面這個(gè)會(huì)打印出什么呢?在第一次發(fā)送消息的時(shí)候[subject sendNext:@"666 666"];,會(huì)打印出666,當(dāng)調(diào)用到[subject sendNext:@"888888"];,因?yàn)閟ubject可以多次訂閱,所以不僅第二次會(huì)打印,第一次同樣也會(huì)打印出。所以最后打印出

2021-04-08 15:38:37.497503+0800 LBDaySurgery(Dev)[10525:3913448] 收到的消息是:---666 666
2021-04-08 15:38:37.497542+0800 LBDaySurgery(Dev)[10525:3913448] 收到的消息是:---888888
2021-04-08 15:38:37.497555+0800 LBDaySurgery(Dev)[10525:3913448] 第二次收到:--888888

我們?cè)倏聪旅孢@段代碼

-(void)creatracSubject{
    
    RACSubject *subject=[RACSubject subject];
    [subject sendNext:@"888888"];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"收到的消息是:---%@",x);
    }];
    
}

一運(yùn)行發(fā)現(xiàn)沒(méi)打印出來(lái),這是為什么呢?這是因?yàn)樵诎l(fā)送消息是要遍歷存放訂閱者的數(shù)組,但是這個(gè)數(shù)組現(xiàn)在是空的,它只有在訂閱消息的時(shí)候才會(huì)加入到數(shù)組,自然就不會(huì)發(fā)送消息,也就不會(huì)打印出結(jié)果。
如果非要把順序?qū)懗上劝l(fā)送消息 在訂閱,那么我們可以用RACReplaySubject來(lái)實(shí)現(xiàn)

-(void)creatReplaySubject{
    
    RACReplaySubject *subject=[RACReplaySubject subject];
    [subject sendNext:@"888888"];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"收到的消息是:---%@",x);
    }];
    
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容