ReactiveCocoa信号发送详解

简介

ReactiveCocoa 是一个重型的 FRP (Functional Reactive Programming 是一种响应变化的编程范式) 框架。内部使用了大量的block。FRP的核心就是信号。
RACSignal就是信号,是ReactiveCocoa中很重要的一个概念。RACSignal本体是RACStream。信号就是数据流,可以用来传递和绑定。

以下代码基于V2.5的ReactiveCocoa

创建RACsignal

不说废话,先来一张图

ReactiveCocoa信号发送详解_第1张图片
RACSignal.png
// 源码
+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

通过RACDynamicSignal创建信号,此时传入一个block,这个block的参数是一个遵循RACSubscriber协议的一个变量,同时这个block的返回值是一个RACDisposable类型。

通过源码分析,看到RACDynamicSignal有一个属性didSubscribe存储了传进来的的block,这个属性将在之后订阅的时候使用。

这个RACSubscriber的协议,其中定义了几个方法

@protocol RACSubscriber 
@required

// 发送next需要执行的参数
- (void)sendNext:(id)value;
// 发送错误
- (void)sendError:(NSError *)error;
// 发送成功
- (void)sendCompleted;
// 处理信号,是否释放取消订阅。
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;

@end

创建一个信号

RACSignal *aSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@"a"];
    [subscriber sendCompleted];
    return [RACDisposable disposableWithBlock:^{
        
    }];
}];

订阅

一个信号通过调用subscribeNext创建一个subscriber进行subscription。

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

// 当前self是RACDynamicSignal所以,使用RACDynamicSignal.m中的subscribe:方法。
// RACDynamicSignal.m
- (RACDisposable *)subscribe:(id)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;
}
  1. 源码中创建了一个RACSubscriber来存储nextBlock、error、completed。然后订阅处理subscription。

  2. 这里有一个RACCompoundDisposable,这是一个RACDisposable,只不过RACCompoundDisposable可以存放多个RACDisposable 。当RACCompoundDisposable 执行dispose方法时,它所存放的disposable都会被释放。

  3. 使用RACPassthroughSubscriber将当前的订阅者进行转化,转化为另外一种形式的订阅者。这个订阅者中存储了当前的订阅者,信号、disposable。存储了一个信号的完整处理,并且这个订阅者同样遵循协议。这里可以把RACPassthroughSubscriber当成是订阅者的装饰器(伪装器)。

  4. 使用RACPassthroughSubscriber的目的是将subscirber传递给另一个还没有disposed的subscriber。

    Passes through all events to another subscriber while not disposed.

  5. 当执行self.didSubscribe(subscriber)时siganle存储的block就会被执行。当sendNext:执行时,先执行[RACPassthroughSubscriber sendNext:],然后调用RACPassthroughSubscriber中的subscriber来执行sendNext:

    // 源码
        // 源码
    - (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]));
       }
    
       [self.innerSubscriber sendNext:value];
    

}
继续执行addDisposable,此时会将RACCompoundDisposable```释放。

```objc
// 源码
- (void)addDisposable:(RACDisposable *)disposable {
NSCParameterAssert(disposable != self);
if (disposable == nil || disposable.disposed) return;

BOOL shouldDispose = NO;

OSSpinLockLock(&_spinLock);
{
    if (_disposed) {
        shouldDispose = YES;
    } else {
        #if RACCompoundDisposableInlineCount
        for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
            if (_inlineDisposables[i] == nil) {
                _inlineDisposables[i] = disposable;
                goto foundSlot;
            }
        }
        #endif

        if (_disposables == NULL) _disposables = RACCreateDisposablesArray();
        CFArrayAppendValue(_disposables, (__bridge void *)disposable);

        if (RACCOMPOUNDDISPOSABLE_ADDED_ENABLED()) {
            RACCOMPOUNDDISPOSABLE_ADDED(self.description.UTF8String, disposable.description.UTF8String, CFArrayGetCount(_disposables) + RACCompoundDisposableInlineCount);
        }

    #if RACCompoundDisposableInlineCount
    foundSlot:;
    #endif
    }
}
OSSpinLockUnlock(&_spinLock);

// Performed outside of the lock in case the compound disposable is used
// recursively.
// 会在此处释放。
if (shouldDispose) [disposable dispose];

}


6. 源码中可以看出,订阅一个信号,返回的是一个RACDisposable,作为一个返回值返回到外部,我们可以在外部对其取消这个订阅。

###总结
```objc
// part 1.
RACSignal *aSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    // 此处subscriber为转换后的subscriber
    // part 5.
   [subscriber sendNext:@"abc"];
   [subscriber sendCompleted];
    
   // part 6.
   return [RACDisposable disposableWithBlock:^{
       NSLog(@"disposable");
   }];
}];

// part 2.
RACDisposable *adisposable = [aSignal subscribeNext:^(id x) {
   NSLog(@"~~~~~~~~~~~  %@",x);
}];
  1. 调用createSignal:创建一个信号。存储当前的block到didSubscribe这个block中。
  2. 调用subscribeNext:订阅信号。创建一个subscriber来subscription。在subscriber中存储nextBlock,errorBlock,completeBlock三个block。
  3. 当前的订阅通过转换,成为RACPassthroughSubscriber,这个subscriber中有三个重要的属性分别是当前订阅的subscriber,当前的signal和RACCompoundDisposable。
  4. RACDynamicSignal执行didSubscribe(RACPassthroughSubscriber)这个block。执行RACPassthroughSubscriber中的sendNext:, sendError:, sendCompeleted
  5. RACPassthroughSubscriber中通过判断当前的disposable的状态来判断是否告诉subscriber调用相应的sendNext:等。
  6. 调用dispose方法,完成整个过程。

用一张图来来表示整个过程


ReactiveCocoa信号发送详解_第2张图片
ReactiveCocoa.jpg

引用

ReactiveCocoa gitHub

美团点评技术团队RACSignal的Subscription深入分析

iOS ReactiveCocoa详解

写在最后

第一篇ReactiveCocoa的文章,写的不好,如有写的不对的地方,欢迎指正,共同进步。谢谢!!!

你可能感兴趣的:(ReactiveCocoa信号发送详解)