史上最全ReactiveCocoa(RAC)之信號(hào)類(lèi)源碼解析

信號(hào)signal是RAC的絕對(duì)核心,所有的操作都是圍繞著信號(hào)來(lái)處理的。
比如:創(chuàng)建信號(hào),訂閱信號(hào),發(fā)送信號(hào)是消息發(fā)送的核心步驟。
常見(jiàn)的三個(gè)信號(hào)類(lèi)為:

  • RACSignal
  • RACSubject
  • RACReplaySubject

一、RACSignal

代碼實(shí)現(xiàn):

// 1.創(chuàng)建信號(hào)
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
       // 注:block在此僅僅是個(gè)參數(shù),未被調(diào)用,
       //當(dāng)有訂閱者訂閱信號(hào)時(shí)會(huì)調(diào)用block。
// 2.發(fā)送信號(hào)
        [subscriber sendNext:@1];
        // 如果不在發(fā)送數(shù)據(jù),最好發(fā)送信號(hào)完成,內(nèi)部會(huì)自動(dòng)調(diào)用[RACDisposable disposable]取消訂閱信號(hào)。
        return nil;
    }];
// 3.訂閱信號(hào),才會(huì)激活信號(hào).
    [siganl subscribeNext:^(id x) {
        // block調(diào)用時(shí)刻:每當(dāng)有信號(hào)發(fā)出數(shù)據(jù),就會(huì)調(diào)用block.
        NSLog(@"接收到數(shù)據(jù):%@",x);
    }];

使用步驟:

1.創(chuàng)建信號(hào)

+(RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;```

內(nèi)部原理:

* 1.1 方法解析:方法名為createSignal:其返回值類(lèi)型為RACSignal類(lèi)的實(shí)例變量。參數(shù)為一個(gè)名字為didSubscribe的block。
* 1.2 方法參數(shù)didSubscribe解析:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {}的返回值是RACDisposable類(lèi)型的實(shí)例變量,參數(shù)為id類(lèi)型且遵守且遵守RACSubscriber協(xié)議的subscriber。
* 1.3 創(chuàng)建信號(hào),首先把didSubscribe這個(gè)block保存到信號(hào)中,但不會(huì)觸發(fā)。此時(shí)didSubscribe僅僅作為方法的參數(shù),并沒(méi)有被觸發(fā),所以信號(hào)也僅僅是一個(gè)冷信號(hào),block內(nèi)部不會(huì)執(zhí)行。(具體執(zhí)行見(jiàn)下文)

源碼解析:

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
//創(chuàng)建了一個(gè)RACDynamicSignal類(lèi)的信號(hào)
RACDynamicSignal *signal = [[self alloc] init];
//將代碼塊保存到信號(hào)里面(但此時(shí)僅僅是保存,沒(méi)有調(diào)用)
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}

2.訂閱信號(hào)

(激活信號(hào),冷信號(hào)變成熱信號(hào))

  • (RACDisposable *)subscribeNext:(void (^ )(id x))nextBlock;
* 2.1當(dāng)信號(hào)被訂閱,也就是調(diào)用signal的subscribeNext:nextBlock,
* 2.2nextBlock內(nèi)部創(chuàng)建了訂閱者subscriber,并且把nextBlock保存到subscriber中。
//  RACSignal.h
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
 //內(nèi)部創(chuàng)建了RACSubscriber(訂閱者)類(lèi)的實(shí)例對(duì)象o,并且將nextBlock保存到o中,在返回值出執(zhí)行o,實(shí)際也是執(zhí)行了nextBlock。
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o]; //內(nèi)部執(zhí)行了nextBlock,具體見(jiàn)下文
}

+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
 RACSubscriber *subscriber = [[self alloc] init];
//將block保存到subscriber中
 subscriber->_next = [next copy];
 subscriber->_error = [error copy];
 subscriber->_completed = [completed copy];

 return subscriber;
}

//執(zhí)行
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
 NSCParameterAssert(subscriber != nil);

 RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
 subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
//判斷有無(wú)self.didSubscribe,有則執(zhí)行該self.didSubscribe,意味著將訂閱者subscriber發(fā)送過(guò)去
 if (self.didSubscribe != NULL) {
  RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
   RACDisposable *innerDisposable = self.didSubscribe(subscriber);
   [disposable addDisposable:innerDisposable];
  }];

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


3.發(fā)送信號(hào)

訂閱信號(hào)時(shí),將subscriber傳遞給didSubscribe的參數(shù)subscriber

利用[subscriber sendNext:@1]方法發(fā)送信號(hào)。

//  RACSubscriber.h
// These callbacks should only be accessed while synchronized on self.
@property (nonatomic, copy) void (^next)(id value);

//名為next的block是返回值為void,參數(shù)為id類(lèi)型的value,在sendNext:內(nèi)部,將next復(fù)制給nextBlock,執(zhí)行該方法后,subscribeNext:的block參數(shù)才會(huì)被調(diào)用。

- (void)sendNext:(id)value {
 @synchronized (self) {
  void (^nextBlock)(id) = [self.next copy];
  if (nextBlock == nil) return;
  //執(zhí)行nextBlock,發(fā)送value
  nextBlock(value);
 }
}

RACSignal原理流程圖:

RACSignal實(shí)現(xiàn)原理圖.png

RACSignal總結(jié):

三步驟(先創(chuàng)建信號(hào),然后訂閱信號(hào),最后執(zhí)行didSubscribe內(nèi)部的方法)順序是不能變的。

RACSignal底層實(shí)現(xiàn):

* 1.創(chuàng)建信號(hào),首先把didSubscribe保存到信號(hào)中,還不會(huì)觸發(fā)。
* 2.當(dāng)信號(hào)被訂閱,也就是調(diào)用signal的subscribeNext:nextBlock
  2.1 subscribeNext內(nèi)部會(huì)創(chuàng)建訂閱者subscriber,并且把nextBlock保存到subscriber中。
   2.2 subscribeNext內(nèi)部會(huì)調(diào)用siganl的didSubscribe
* 3.siganl的didSubscribe中調(diào)用[subscriber sendNext:@1];
* 3.1 sendNext底層其實(shí)就是執(zhí)行subscriber的nextBlock

二、RACSubject

RACSubject:信號(hào)提供者,自己可以充當(dāng)信號(hào),又能發(fā)送信號(hào)。

先來(lái)簡(jiǎn)單回顧下,RACSignal類(lèi)中發(fā)送和訂閱信號(hào)是兩步完成的。

//發(fā)送信號(hào)的為遵守RACSubscribe協(xié)議的對(duì)象subscriber完成發(fā)送
[subscriber sendNext:@1];
//訂閱信號(hào)的為RACSignal的實(shí)例對(duì)象
[siganl subscribeNext:^(id x) {};
  • 疑問(wèn):有沒(méi)有辦法讓一個(gè)對(duì)象既能發(fā)送也可以發(fā)送消息呢?

其實(shí)很簡(jiǎn)單,只要讓具有RACSignal對(duì)象遵守RACSubscribe協(xié)議,就既能發(fā)送,又能訂閱信號(hào)了。
這個(gè)類(lèi)就是RACSubject。雖然和RACSignal一樣都具有訂閱和發(fā)送信號(hào)的能力,但其內(nèi)部原理不同。
下文是對(duì)RACSubject類(lèi)進(jìn)行剖析。

代碼實(shí)現(xiàn)

調(diào)用順序不變,但是可以創(chuàng)建多個(gè)訂閱者,并發(fā)送信號(hào);

 // 1.創(chuàng)建信號(hào)
    RACSubject *subject = [RACSubject subject];   
// 2.訂閱信號(hào)(這里可以創(chuàng)建多個(gè)訂閱者)
    [subject subscribeNext:^(id x) {
        // block調(diào)用時(shí)刻:當(dāng)信號(hào)發(fā)出新值,就會(huì)調(diào)用.
        NSLog(@"第一個(gè)訂閱者%@",x);
    }];
    [subject subscribeNext:^(id x) {
        // block調(diào)用時(shí)刻:當(dāng)信號(hào)發(fā)出新值,就會(huì)調(diào)用.
        NSLog(@"第二個(gè)訂閱者%@",x);
    }];
     [subject subscribeNext:^(id x) {
        // block調(diào)用時(shí)刻:當(dāng)信號(hào)發(fā)出新值,就會(huì)調(diào)用.
        NSLog(@"第三個(gè)訂閱者%@",x);
    }];
// 3.發(fā)送信號(hào)
    [subject sendNext:@"1"];

源碼解析

1.創(chuàng)建信號(hào)

//  RACSubject.m
+ (instancetype)subject {
 return [[self alloc] init];
}

- (id)init {
 self = [super init];
 if (self == nil) return nil;
 
 _disposable = [RACCompoundDisposable compoundDisposable];
 
 _subscribers = [[NSMutableArray alloc] initWithCapacity:1];
 
 return self;
}

2.訂閱信號(hào)

不同類(lèi)型的信號(hào),創(chuàng)建訂閱者的方式不同,RACSignal訂閱信號(hào)時(shí),調(diào)用了形影的block。

而RACSubject訂閱信號(hào)的實(shí)質(zhì)就是將內(nèi)部創(chuàng)建的訂閱者保存在訂閱者數(shù)組self.subscribers中,僅此而已。訂閱者對(duì)象有一個(gè)名為nextBlock的block參數(shù)。

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
 NSCParameterAssert(nextBlock != NULL);
 
 RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
 return [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) {
 //將訂閱者保存在訂閱者數(shù)組中
  [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];
  }
 }];
}

3.發(fā)送信號(hào)

底層實(shí)現(xiàn)是:

  1. 先遍歷訂閱者數(shù)組中的訂閱者;
  2. 后執(zhí)行訂閱者中的nextBlock;
  3. 最后讓訂閱者發(fā)送信號(hào)。
//  RACSubject.m

- (void)sendNext:(id)value {
//順序A:遍歷保存在數(shù)組中的訂閱者對(duì)象
 [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
//順序E:調(diào)用訂閱者,執(zhí)行sendNext:方法
  [subscriber sendNext:value];
 }];
}

//順序B:遍歷subscribers數(shù)組中的訂閱者對(duì)象,執(zhí)行block。
- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
 NSArray *subscribers;
 @synchronized (self.subscribers) {
  subscribers = [self.subscribers copy];
 }
//順序C:遍歷數(shù)組,取出subscriber的block,并且執(zhí)行。
 for (id<RACSubscriber> subscriber in subscribers) {
//順序D,執(zhí)行block
  block(subscriber);
 }
}

RACSubject原理流程圖:

RACSubject原理圖 .png

總結(jié)

RACSubscriber:表示訂閱者的意思,用于發(fā)送信號(hào),這是一個(gè)協(xié)議,不是一個(gè)類(lèi),只要遵守這個(gè)協(xié)議,并且實(shí)現(xiàn)方法才能成為訂閱者。通過(guò)create創(chuàng)建的信號(hào),都有一個(gè)訂閱者,幫助他發(fā)送數(shù)據(jù)。

RACDisposable:用于取消訂閱或者清理資源,當(dāng)信號(hào)發(fā)送完成或者發(fā)送錯(cuò)誤的時(shí)候,就會(huì)自動(dòng)觸發(fā)它。

RACSubject:底層實(shí)現(xiàn)和RACSignal不一樣。
具體如下:

  • 1.調(diào)用subscribeNext訂閱信號(hào),只是把訂閱者保存起來(lái),并且訂閱者的nextBlock已經(jīng)賦值了。
  • 2.調(diào)用sendNext發(fā)送信號(hào),遍歷剛剛保存的所有訂閱者,一個(gè)一個(gè)調(diào)用訂閱者的nextBlock。
  • 3.由于本質(zhì)是將訂閱者保存到數(shù)組中,所以可以有多個(gè)訂閱者訂閱信息。

使用場(chǎng)景:不想監(jiān)聽(tīng)某個(gè)信號(hào)時(shí),可以通過(guò)它主動(dòng)取消訂閱信號(hào)。
RACSubject:信號(hào)提供者,自己可以充當(dāng)信號(hào),又能發(fā)送信號(hào)。

使用場(chǎng)景:通常用來(lái)代替代理,有了它,就不必要定義代理了。

缺點(diǎn):
還是必須先訂閱,后發(fā)送信息。訂閱信號(hào)就是創(chuàng)建訂閱者的過(guò)程,如果不先訂閱,數(shù)組中就沒(méi)有訂閱者對(duì)象,那就通過(guò)訂閱者發(fā)送消息。


三、RACReplaySubject

介紹

上文中提到了,RACSubject要求先訂閱,后發(fā)送信號(hào)。

代碼實(shí)現(xiàn):

// 1.創(chuàng)建信號(hào)
    RACReplaySubject *subject = [RACReplaySubject subject];
// 2.訂閱信號(hào)
    [subject subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    // 遍歷所有的值,拿到當(dāng)前訂閱者去發(fā)送數(shù)據(jù)
// 3.發(fā)送信號(hào)
    [subject sendNext:@1];
    // RACReplaySubject發(fā)送數(shù)據(jù):
    // 1.保存值
    // 2.遍歷所有的訂閱者,發(fā)送數(shù)據(jù)
    
    // RACReplaySubject:可以先發(fā)送信號(hào),在訂閱信號(hào)

使用步驟:

1.創(chuàng)建信號(hào)

//  RACReplaySubject.m

+ (instancetype)subject {
 return [[self alloc] init];
}

- (instancetype)init {
 return [self initWithCapacity:RACReplaySubjectUnlimitedCapacity];
}
//此時(shí)調(diào)用的子類(lèi)RACReplaySubject的初始化方法
- (instancetype)initWithCapacity:(NSUInteger)capacity {
 self = [super init];
 if (self == nil) return nil;
 
 _capacity = capacity;
//會(huì)用_valuesReceived這個(gè)數(shù)組保存值value
 _valuesReceived = (capacity == RACReplaySubjectUnlimitedCapacity ? [NSMutableArray array] : [NSMutableArray arrayWithCapacity:capacity]);
 
 return self;
}

2.訂閱信號(hào)

遍歷拿到保存在數(shù)組中的所有值,然后調(diào)用subscriber發(fā)送信號(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)建訂閱者的方式不同
}

//RACReplaySubject類(lèi)對(duì)象執(zhí)行[self subscribe:o]
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
 RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

 RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
  @synchronized (self) {
//先遍歷self.valuesReceived的所有的值,后讓subscriber調(diào)用sendNext:發(fā)送信號(hào)。
   for (id value in self.valuesReceived) {
    if (compoundDisposable.disposed) return;

    [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 {
    RACDisposable *subscriptionDisposable = [super subscribe:subscriber];
    [compoundDisposable addDisposable:subscriptionDisposable];
   }
  }
 }];

 [compoundDisposable addDisposable:schedulingDisposable];

 return compoundDisposable;
}

3.發(fā)送信號(hào)

- (void)sendNext:(id)value {
 @synchronized (self) {
//重點(diǎn):發(fā)送信號(hào)的時(shí)候,會(huì)先將值value保存到數(shù)組中,
  [self.valuesReceived addObject:value ?: RACTupleNil.tupleNil];
//調(diào)用父類(lèi)發(fā)送(先遍歷訂閱者,然后發(fā)送值value)
  [super sendNext:value];
  
  if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) {
   [self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)];
  }
 }
}

RACReplaySubject原理圖

RACReplaySubject原理圖.jpg

RACReplaySubject總結(jié)

RACReplaySubject是RACSubject的子類(lèi)

RACReplaySubject使用步驟:

// 1.創(chuàng)建信號(hào) [RACSubject subject],跟RACSiganl不一樣,創(chuàng)建信號(hào)時(shí)沒(méi)有block。
// 2.可以先訂閱信號(hào),也可以先發(fā)送信號(hào)。
// 2.1 訂閱信號(hào) - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 2.2 發(fā)送信號(hào) sendNext:(id)value
最后編輯于
?著作權(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)容