RAC

RACSubject

//创建信号
RACSubject *subject = [RACSubject subject];
//订阅信号
[subject subscribeNext:^(id  _Nullable x) {
      NSLog(@"subscribeNext %@",x);
}];
//发送信号 
[subject sendNext:@"1"];

分析

step1: 先看下RACSubject

@interface RACSubject : RACSignal 

/// Returns a new subject.
+ (instancetype)subject;

// Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
- (void)sendNext:(nullable ValueType)value;

@end

发现RACSubject继承于RACSignal,有一个初始化方法subject和一个发送信号的方法 sendNext

step2: 查看subject方法

+ (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;
}

调用了[[self alloc] init]方法,并对init方法进行重写,在init中对两个属性进行了初始化

  • 看下属性的声明
// Contains all current subscribers to the receiver.
//
// This should only be used while synchronized on `self`.
@property (nonatomic, strong, readonly) NSMutableArray *subscribers;

// Contains all of the receiver's subscriptions to other signals.
@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;

用一个数组保存所有的订阅者,另一个对象用来取消订阅

step3: 查看订阅subscribeNext:方法,这是父类的方法

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    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;
}

创建一个订阅者,将传进来的参数赋值给订阅者的成员变量,在例子中就是将订阅的block与订阅者关联上。

  • subscribe 调用的是RACSubject类中的subscribe
- (RACDisposable *)subscribe:(id)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 obj, NSUInteger index, BOOL *stop) {
                return obj == subscriber;
            }];

            if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
        }
    }]];

    return disposable;
}

将上一步创建的订阅者保存在第一步创建的数组中

step4: 查看sendNext:方法

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

遍历所有的订阅者

  • 查看遍历方法
- (void)enumerateSubscribersUsingBlock:(void (^)(id subscriber))block {
    NSArray *subscribers;
    @synchronized (self.subscribers) {
        subscribers = [self.subscribers copy];
    }

    for (id subscriber in subscribers) {
        block(subscriber);
    }
}

self.subscribers是第一步中创建的数组,遍历数组将其中的订阅者对象取出来,调用传进来的block方法。

  • 查看遍历回调中的sendNext,这是调用的RACSubscriber的方法
- (void)sendNext:(id)value {
    @synchronized (self) {
        void (^nextBlock)(id) = [self.next copy];
        if (nextBlock == nil) return;

        nextBlock(value);
    }
}

从订阅者对象中取出next变量,即第二步中订阅信号时的block,然后调用这个block方法

RACSignal

既然从前面得知RACSubject继承于RACSignal,那我们就研究下RACSignal

// 1.创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
        // 3.发送信号
        [subscriber sendNext:@"liu"];
        //发送完成信号 并取消订阅
        [subscriber sendCompleted];
        // 4.取消信号,如果信号想要被取消,就必须返回一个RACDisposable
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"取消订阅");
        }];
    }];
    
    [signal subscribeNext:^(id x) {
        // block的调用时刻:只要信号内部发出数据就会调用这个block
        NSLog(@"======%@", x);
    }];

运行结果

源码分析

step1: 查看createSignal方法

+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {
    return [RACDynamicSignal createSignal:didSubscribe];
}

//简单看下 RACDynamicSignal
//一个私有的RACSignal子类实现它们的订阅行为
// A private `RACSignal` subclasses that implements its subscription behavior
// using a block.
@interface RACDynamicSignal : RACSignal

+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe;

@end
  • 查看RACDynamicSignal类中的createSignal方法
+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

这个方法中主要做了两件事

  • 1、创建一个信号量对象
  • 2、将外面传进来的block保存在信号量对象的_didSubscribe变量中

step2: 查看订阅信号subscribeNext

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

这个方法主要做了两件事

  • 1、创建订阅者

  • 2、执行订阅命令

  • subscriberWithNext: error: completed:前面已经讲过,这里就不再讲述

  • 查看subscribe:方法,是在RACDynamicSignal类中

- (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;
}

关键代码在self.didSubscribe(subscriber);,在创建信号量时将传进来的block 赋值给了self.didSubscribe,此时调用self.didSubscribe(subscriber);即调用block并将订阅者对象传进去

问题是self.didSubscribe(subscriber);的调用这是在block中,那这个block是在什么时候调用?

  • 查看schedule方法,是RACSubscriptionScheduler类的方法
- (RACDisposable *)schedule:(void (^)(void))block {
    NSCParameterAssert(block != NULL);

    if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block];

    block();
    return nil;
}

可以看到传进来的参数block被调用

step3: sendNext发送信号

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

        nextBlock(value);
    }
}

从订阅者对象中取出next变量,即第二步中订阅信号时的block,然后调用这个block方法

RACReplaySubject

RACReplaySubject *subject = [RACReplaySubject subject];
[subject subscribeNext:^(id  _Nullable x) {
     NSLog(@"subscribeNext - %@",x);
}];
[subject sendNext:@"liu"];

运行结果

subscribeNext - liu

源码分析

step1: 先看下它的.h文件

保存它发送的值
/// A replay subject saves the values it is sent (up to its defined capacity)
/// and resends those to new subscribers. It will also replay an error or
/// completion.
@interface RACReplaySubject : RACSubject

/// Creates a new replay subject with the given capacity. A capacity of
/// RACReplaySubjectUnlimitedCapacity means values are never trimmed.
+ (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity;

@end

RACReplaySubjectRACSubject的子类,对外只暴露了一个replaySubjectWithCapacity方法,这是个初始化方法

step2: 看下replaySubjectWithCapacity方法实现

+ (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity {
    return [(RACReplaySubject *)[self alloc] initWithCapacity:capacity];
}

- (instancetype)init {
    return [self initWithCapacity:RACReplaySubjectUnlimitedCapacity];
}

- (instancetype)initWithCapacity:(NSUInteger)capacity {
    self = [super init];
    
    _capacity = capacity;
    _valuesReceived = (capacity == RACReplaySubjectUnlimitedCapacity ? [NSMutableArray array] : [NSMutableArray arrayWithCapacity:capacity]);
    
    return self;
}

不管是通过自己的初始化方法replaySubjectWithCapacity,还是通过父类的初始化方法subject,最终都会调用initWithCapacity方法,在initWithCapacity方法里创建了一个数组用来存储发送信号时传递的数据。

step2: 订阅信号subscribeNext

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}
  • 第一步创建订阅者与父类是相同的,创建一个订阅者并将block保存在订阅者的成员变量中。

  • 第二步调用的RACReplaySubject自己的方法

- (RACDisposable *)subscribe:(id)subscriber {
    RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

    RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
        @synchronized (self) {
            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;
}

会遍历第一步创建的数组保存的数据,然后发送信号,即订阅信号流程中包含发送信号

step3: 发送信号sendNext,注意这是在RACReplaySubject中实现

- (void)sendNext:(id)value {
    @synchronized (self) {
        [self.valuesReceived addObject:value ?: RACTupleNil.tupleNil];
        
        if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) {
            [self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)];
        }
        
        [super sendNext:value];
    }
}

会将发送的数据保存在第一步创建的数组中以便订阅信号时使用,最终发送信号还是调用的父类的方法。

问题: RACReplaySubjectRACSubject有什么区别

RACSubject中代码必须按照 创建信号--> 订阅信号 --> 发送信号,这样的先后顺序,否则无效;而在RACReplaySubject创建信号后,订阅信号发送信号顺序无所谓。

所以RACReplaySubject写成下面这样也是可以的。

RACReplaySubject *subject = [RACReplaySubject subject];
[subject sendNext:@"liu"];
[subject subscribeNext:^(id  _Nullable x) {
      NSLog(@"subscribeNext - %@",x);
}];

RACCommand

- (void)testRACCommand {
    
    /**
     *  RACCommand使用注意
     *  1、RACCommand内部必须返回RACSignal
     *  2、executionSignals信号中的信号,一开始获取不到内部信号
     *      2.1 使用switchToLatest:获取内部信号
     *      2.2 使用execute:获取内部信号
     *  3、executing判断是否正在执行
     *      3.1 第一次不准确,需要skip:跳过
     *      3.2 一定要记得sendCompleted,否则永远不会执行完成
     *  4、通过执行execute,执行command的block
     */
    
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        return [RACSignal createSignal:^RACDisposable * _Nullable(id  _Nonnull subscriber) {
            [subscriber sendNext:@"发送第1条信号"];
            [subscriber sendCompleted];
            return nil;
        }];
    }];
    
    [command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {

    }];
    
    [[command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
        //NSLog(@"x = %@", x);
        if (x.boolValue) {
            NSLog(@"x = %@ 正在执行", x);
        } else {
            NSLog(@"x = %@ 执行完成", x);
        }
    }];
    
    [command execute:@1];
    
}

源码分析

step1: 查看RACCommand类的.h文件

1. executionSignals:需要执行的block成功的时候返回的信号,他是在主线程执行的。
2. executing:信号量  监听RACCommand的状态
3. enabled:当前命令是否enabled,默认是no,他也可以根据enableSignal来设置或者allowsConcurrentExecution设置为NO的时候(command已经开始执行)
4. errors:执行command的时候获取的error都会通过这个信号发送
5. allowsConcurrentExecution:是否允许并发执行command,默认是NO。

6.initWithSignalBlock:(RACSignal * (^)(id input))signalBlock:初始化RACCommand,参数为返回一个信号的block,即block返回的是executionSignals
7.- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock:第一个参数设置当前command是否可用,第二个是执行的block。enableed默认是yes,所以第二个参数也可以为nil。
8.execute:(id)input:调用command,input为executionSignals的订阅者发送的值

step2: 查看初始化方法

- (instancetype)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock {
    return [self initWithEnabled:nil signalBlock:signalBlock];
}

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

    self = [super init];

    _addedExecutionSignalsSubject = [RACSubject new];
    _allowsConcurrentExecutionSubject = [RACSubject new];
    _signalBlock = [signalBlock copy];

    _executionSignals = [[[self.addedExecutionSignalsSubject
        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 = [[[self.addedExecutionSignalsSubject
        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 = [[[[self.addedExecutionSignalsSubject
        flattenMap:^(RACSignal *signal) {
            return [[[signal
                catchTo:[RACSignal empty]]
                then:^{
                    return [RACSignal return:@-1];
                }]
                startWith:@1];
        }]
        scanWithStart:@0 reduce:^(NSNumber *running, NSNumber *next) {
            return @(running.integerValue + next.integerValue);
        }]
        map:^(NSNumber *count) {
            return @(count.integerValue > 0);
        }]
        startWith:@NO];

    _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:[self.allowsConcurrentExecutionSubject startWith:@NO]
        then:[RACSignal return:@YES]
        else:[immediateExecuting not]];
    
    if (enabledSignal == nil) {
        enabledSignal = [RACSignal return:@YES];
    } else {
        enabledSignal = [enabledSignal startWith:@YES];
    }
    
    _immediateEnabled = [[[[RACSignal
        combineLatest:@[ enabledSignal, moreExecutionsAllowed ]]
        and]
        takeUntil:self.rac_willDeallocSignal]
        replayLast];
    
    _enabled = [[[[[self.immediateEnabled
        take:1]
        concat:[[self.immediateEnabled skip:1] deliverOn:RACScheduler.mainThreadScheduler]]
        distinctUntilChanged]
        replayLast]
        setNameWithFormat:@"%@ -enabled", self];

    return self;
}

将传进来的block拷贝到变量_signalBlock 中,以便后面使用。

step3: 查看command.executing subscribeNext:,订阅信号从而获取RACCommand的状态

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

点击subscribeNext进入的是RACSignal文件,但是查看self会发现是RACReplaySubject

  • 进入RACReplaySubjectsubscribe方法
- (RACDisposable *)subscribe:(id)subscriber {
    RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

    RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
        @synchronized (self) {
            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;
}

step4: RACCommand执行命令execute

- (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);
    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]];
    
    [self.addedExecutionSignalsSubject sendNext:connection.signal];

    [connection connect];
    return [connection.signal setNameWithFormat:@"%@ -execute: %@", self, RACDescription(input)];
}
  • self.signalBlock(input)self.signalBlock是第一步中外部传进来的block,这句代码就是执行外面的block

RACTuple

CocoaReactive 中,有些信号流传递的信号量是由其他多个信号流传递的信号量组合而成的,而这多个信号量则是封装在一个 RACTuple 实例中进行传递的。

你可能感兴趣的:(RAC)