菜单

RAC基础学习一:信号及订阅者模式。ReactiveCocoa 中 RACSignal 是什么发送信号的。

2018年9月19日 - 中甲报道

咱俩当知晓到RAC的雄强以及不可思议的时刻,需要思考两只地方:1、它是怎么样促成的?第二个问题虽然另行产生难度:2、它是何许想到这样设计的?
此地我们事先品尝研究第一只问题,它是哪些贯彻之,分析重点的条。

原文:《掘金专栏 – ReactiveCocoa 中 RACSignal
是怎么样发送信号的》
作者:
一律详实殇流化隐半边冰霜

在RAC里面,我们有着围绕的东西才主体是即刻几种:信号(signal)、订阅者(subscriber)、还有关于信号的生产者实体、信号的主顾,这几只之关系。
咱怎么以RAC,因为其解耦太好了,除此之外,它简洁,配合MVVM能发表出好特别的图等等。相信我们且勾腻了对象期间的繁杂通信、一怪堆状态的创立与治本、越来越难保障的政工逻辑,这些虽是RAC诞生的重任。

章都由此作者授权。

新家总是容易给同样积聚概念来得晕头转向,我怀念其实只是及时几种:

前言

ReactiveCocoa大凡一个(第一个?)将函数响应式编程范例带入Objective-C的开源库。ReactiveCocoa是由于Josh
Abernathy和Justin
Spahr-Summers
两各很神以针对GitHub for
Mac的开销进程被编辑的。Justin
Spahr-Summers
大神在2011年11月13号下午12接触35划分进行的第一坏提交,直到2013年2月13日上午3点05分颁了那1.0
release,达到了第一个基本点里程碑。ReactiveCocoa社区为深活跃,目前新型版本曾成功了ReactiveCocoa
5.0.0-alpha.3,目前以5.0.0-alpha.4付出中。

ReactiveCocoa v2.5
是公认的Objective-C最安定之版,因此吃周边的以OC为要语言的客户端选中采用。ReactiveCocoa
v3.x重点是根据Swift 1.2底版,而ReactiveCocoa v4.x 主要根据Swift
2.x,ReactiveCocoa 5.0即便全盘支持Swift 3.0,也许还有以后的Swift
4.0。接下来几首博客先为ReactiveCocoa
v2.5本子也例,分析一下OC版的RAC具体贯彻(也许分析了了RAC
5.0即来到了)。也算是写于ReactiveCocoa 5.0专业版到前夕的祝福吧。

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
            [subscriber sendNext:@(1)];
            [subscriber sendCompleted];
            return nil;
        }];

目录

  1、createSignal好难啊;2、subscriber是呀?3、这个block什么时调用?

一. 什么是ReactiveCocoa?

ReactiveCocoa(其简称为RAC)是由Github
开源之一个应用叫iOS和OS
X开发的初框架。RAC有函数式编程(FP)和响应式编程(RP)的特性。它至关重要吸取了.Net的
Reactive
Extensions的设计以及落实。

ReactiveCocoa 的宗是Streams of values over time
,随着日变化而持续流淌的数据流。

ReactiveCocoa 主要解决了以下这些问题:

UI控件便需要绑定一个风波,RAC可以充分有利的绑定任何数据流到控件上。

RAC为可交互的UI控件提供了扳平文山会海能够发送Signal信号的计。这些多少流会在用户交互中相互传送。

出了RAC的绑定后,可以不用当关切各种复杂的状态,isSelect,isFinish……也化解了这些状态在末很麻烦保障的问题。

OC中编程原来消息传递机制来以下几种:Delegate,Block
Callback,Target-Action,Timers,KVO,objc上生同样首关于OC中即5栽消息传递方式转什么挑选的文章Communication
Patterns,推荐大家读。现在产生矣RAC之后,以上就5种方法都可合用RAC来拍卖。

[signal subscribeNext:^(id x) {
        if ([x boolValue]) {
            _navView.hidden = YES;
        } else {
            _navView.hidden = NO;
            [UIView animateWithDuration:.5 animations:^{
                _navView.alpha = 1;
            }];
        }
    }];

二. RAC中之核心RACSignal

ReactiveCocoa
中极中心之定义有就是是信号RACStream。RACRACStream中出零星个子类——RACSignal
和 RACSequence。本文先来分析RACSignal。

咱们见面时来看以下的代码:

RACSignal *signal = [RACSignal createSignal:
                     ^RACDisposable *(id<RACSubscriber> subscriber)
{
    [subscriber sendNext:@1];
    [subscriber sendNext:@2];
    [subscriber sendNext:@3];
    [subscriber sendCompleted];
    return [RACDisposable disposableWithBlock:^{
        NSLog(@"signal dispose");
    }];
}];
RACDisposable *disposable = [signal subscribeNext:^(id x) {
    NSLog(@"subscribe value = %@", x);
} error:^(NSError *error) {
    NSLog(@"error: %@", error);
} completed:^{
    NSLog(@"completed");
}];

[disposable dispose];

及时是一个RACSignal被订阅的总体过程。被订阅的经过中,究竟出了啊?

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

RACSignal调用createSignal的时候,会调用RACDynamicSignal的createSignal的方法。

betway体育 1

RACDynamicSignal是RACSignal的子类。createSignal后面的参数是一个block。

(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe

block的返回值是RACDisposable类型,block名叫didSubscribe。block的绝无仅有一个参数是id<RACSubscriber>类型的subscriber,这个subscriber是得遵循RACSubscriber协议的。

RACSubscriber是一个说道,其下发出以下4只商量方式:

@protocol RACSubscriber <NSObject>
@required

- (void)sendNext:(id)value;
- (void)sendError:(NSError *)error;
- (void)sendCompleted;
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;

@end

就此新建Signal的任务就是全部获于了RACSignal的子类RACDynamicSignal上了。

@interface RACDynamicSignal ()
// The block to invoke for each subscriber.
@property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);
@end

RACDynamicSignal这个看似非常简短,里面纵使封存了一个名叫didSubscribe的block。

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

是法中新建了一个RACDynamicSignal对象signal,并将染进的didSubscribe这个block保存进刚刚新建对象signal里面的didSubscribe属性中。最后又叫signal命名+createSignal:。

- (instancetype)setNameWithFormat:(NSString *)format, ... {
 if (getenv("RAC_DEBUG_SIGNAL_NAMES") == NULL) return self;

   NSCParameterAssert(format != nil);

   va_list args;
   va_start(args, format);

   NSString *str = [[NSString alloc] initWithFormat:format arguments:args];
   va_end(args);

   self.name = str;
   return self;
}

setNameWithFormat是RACStream里面的计,由于RACDynamicSignal继承自RACSignal,所以她呢会调用这个措施。

betway体育 2

RACSignal的block就如此被保存起来了,那什么时会让实施为?

betway体育 3

block闭包在订阅的下才会于“释放”出来。

RACSignal调用subscribeNext方法,返回一个RACDisposable。

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
   NSCParameterAssert(nextBlock != NULL);
   NSCParameterAssert(errorBlock != NULL);
   NSCParameterAssert(completedBlock != NULL);

   RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];
   return [self subscribe:o];
}

以斯方式中见面新建一个RACSubscriber对象,并传nextBlock,errorBlock,completedBlock。

@interface RACSubscriber ()

// These callbacks should only be accessed while synchronized on self.
@property (nonatomic, copy) void (^next)(id value);
@property (nonatomic, copy) void (^error)(NSError *error);
@property (nonatomic, copy) void (^completed)(void);
@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;

@end

RACSubscriber这个仿佛非常简短,里面独自出4个属性,分别是nextBlock,errorBlock,completedBlock和一个RACCompoundDisposable信号。

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

betway体育 4

subscriberWithNext方法将传播的3个block都封存分别保存及自己相应之block中。

RACSignal调用subscribeNext方法,最后return的时候,会调用[self
subscribe:o],这里实在是调用了RACDynamicSignal类里面的subscribe方法。

- (RACDisposable *)subscribe:(id<RACSubscriber>)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;
}

RACDisposable有3独子类,其中一个虽是RACCompoundDisposable。

betway体育 5

@interface RACCompoundDisposable : RACDisposable
+ (instancetype)compoundDisposable;
+ (instancetype)compoundDisposableWithDisposables:(NSArray *)disposables;
- (void)addDisposable:(RACDisposable *)disposable;
- (void)removeDisposable:(RACDisposable *)disposable;
@end

RACCompoundDisposable虽然是RACDisposable的子类,但是它其中可以入多只RACDisposable对象,在必要的下可以一口气都调用dispose方法来销毁信号。当RACCompoundDisposable对象被dispose的时节,也会见活动dispose容器内之具备RACDisposable对象。

RACPassthroughSubscriber是一个私的近乎。

@interface RACPassthroughSubscriber : NSObject <RACSubscriber>
@property (nonatomic, strong, readonly) id<RACSubscriber> innerSubscriber;
@property (nonatomic, unsafe_unretained, readonly) RACSignal *signal;
@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;
- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable;
@end

RACPassthroughSubscriber类就惟有马上一个法。目的就是是为了拿具备的信号事件从一个订阅者subscriber传递给任何一个尚没有disposed的订阅者subscriber。

RACPassthroughSubscriber类中保留了3单非常重要之目标,RACSubscriber,RACSignal,RACCompoundDisposable。RACSubscriber是内需转发的信号的订阅者subscriber。RACCompoundDisposable是订阅者的灭绝对象,一旦它们叫disposed了,innerSubscriber就更为经受不顶事件流了。

此间需要留意的凡里面还保留了一个RACSignal,并且它的性质是unsafe_unretained。这里与其它两只属性有分,
其他两独特性都是strong的。这里用不是weak,是以引用RACSignal仅仅只是一个DTrace
probes动态跟踪技术之探针。如果设置成weak,会招致没必要的属性损失。所以这里只有是unsafe_unretained就够了。

- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
   NSCParameterAssert(subscriber != nil);

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

   _innerSubscriber = subscriber;
   _signal = signal;
   _disposable = disposable;

   [self.innerSubscriber didSubscribeWithDisposable:self.disposable];
   return self;
}

回来RACDynamicSignal类里面的subscribe方法中,现在新建好了RACCompoundDisposable和RACPassthroughSubscriber对象了。

 if (self.didSubscribe != NULL) {
  RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
   RACDisposable *innerDisposable = self.didSubscribe(subscriber);
   [disposable addDisposable:innerDisposable];
  }];

  [disposable addDisposable:schedulingDisposable];
 }

RACScheduler.subscriptionScheduler是一个大局的单例。

+ (instancetype)subscriptionScheduler {
   static dispatch_once_t onceToken;
   static RACScheduler *subscriptionScheduler;
   dispatch_once(&onceToken, ^{
    subscriptionScheduler = [[RACSubscriptionScheduler alloc] init];
   });

   return subscriptionScheduler;
}

RACScheduler再持续调整用schedule方法。

- (RACDisposable *)schedule:(void (^)(void))block {
   NSCParameterAssert(block != NULL);
   if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block];
   block();
   return nil;
}


+ (BOOL)isOnMainThread {
 return [NSOperationQueue.currentQueue isEqual:NSOperationQueue.mainQueue] || [NSThread isMainThread];
}

+ (instancetype)currentScheduler {
 RACScheduler *scheduler = NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey];
 if (scheduler != nil) return scheduler;
 if ([self.class isOnMainThread]) return RACScheduler.mainThreadScheduler;

 return nil;
}

于取currentScheduler的进程遭到,会咬定currentScheduler是否是,和是否在主线程中。如果都没,那么就算会调用后台backgroundScheduler去执行schedule。

schedule的入参就是一个block,执行schedule的时光会失掉执行block。也就算是碰头错过实践:

RACDisposable *innerDisposable = self.didSubscribe(subscriber);
   [disposable addDisposable:innerDisposable];

立刻点儿句子关键的言辞。之前信号中保存之block就会在此地被“释放”执行。self.didSubscribe(subscriber)这无异词就尽了信号保存之didSubscribe闭包。

当didSubscribe闭包被起sendNext,sendError,sendCompleted,执行这些语句会分别调用RACPassthroughSubscriber里面对应的计。

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

- (void)sendError:(NSError *)error {
 if (self.disposable.disposed) return;
 if (RACSIGNAL_ERROR_ENABLED()) {
  RACSIGNAL_ERROR(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString(error.description));
 }
 [self.innerSubscriber sendError:error];
}

- (void)sendCompleted {
 if (self.disposable.disposed) return;
 if (RACSIGNAL_COMPLETED_ENABLED()) {
  RACSIGNAL_COMPLETED(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description));
 }
 [self.innerSubscriber sendCompleted];
}

这个时候的订阅者是RACPassthroughSubscriber。RACPassthroughSubscriber里面的innerSubscriber才是终极的实际订阅者,RACPassthroughSubscriber会把价值更持续传递让innerSubscriber。

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

innerSubscriber是RACSubscriber,调用sendNext的时段会预先管自己之self.next闭包copy一客,再调用,而且漫经过要线程安全之,用@synchronized保护正在。最终订阅者的闭包在这边叫调用。

sendError和sendCompleted也都是同理。

小结一下:

betway体育 6

  1. RACSignal调用subscribeNext方法,新建一个RACSubscriber。
  2. 新建的RACSubscriber会copy,nextBlock,errorBlock,completedBlock存在好的性质变量中。
  3. RACSignal的子类RACDynamicSignal调用subscribe方法。
  4. 新建RACCompoundDisposable和RACPassthroughSubscriber对象。RACPassthroughSubscriber分别保存对RACSignal,RACSubscriber,RACCompoundDisposable的援,注意对RACSignal的援是unsafe_unretained的。
  5. RACDynamicSignal调用didSubscribe闭包。先调用RACPassthroughSubscriber的照应的sendNext,sendError,sendCompleted方法。
  6. RACPassthroughSubscriber再去调用self.innerSubscriber,即RACSubscriber的nextBlock,errorBlock,completedBlock。注意这里调用同样是先copy一卖,再调用闭包执行。

  4、subscribeNext又是呀?5、这个block什么时候调用?

三. RACSignal操作的核心bind实现

betway体育 7

以RACSignal的源码里面含了片只基本操作,concat和zipWith。不过当条分缕析这片单操作前,先来分析一下越来越核心的一个函数,bind操作。

优先来说说bind函数的意图:

  1. 会见订阅原始之信号。
  2. 任何时刻原始信号发送一个价值,都见面绑定的block转换一浅。
  3. 要绑定的block转换了价值变成信号,就当下订阅,并把价值发给订阅者subscriber。
  4. 使绑定的block要适可而止绑定,原始之信号就complete。
  5. 当有着的信号都complete,发送completed信号为订阅者subscriber。
  6. 如若中途信号出现了任何error,都使拿这荒唐发送给subscriber

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

 return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
  RACStreamBindBlock bindingBlock = block();

  NSMutableArray *signals = [NSMutableArray arrayWithObject:self];

  RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

  void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) { /*这里暂时省略*/ };
  void (^addSignal)(RACSignal *) = ^(RACSignal *signal) { /*这里暂时省略*/ };

  @autoreleasepool {
   RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
   [compoundDisposable addDisposable:selfDisposable];

   RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
    // Manually check disposal to handle synchronous errors.
    if (compoundDisposable.disposed) return;

    BOOL stop = NO;
    id signal = bindingBlock(x, &stop);

    @autoreleasepool {
     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函数究竟开了哟,写来测试代码:

    RACSignal *signal = [RACSignal createSignal:
                         ^RACDisposable *(id<RACSubscriber> subscriber)
    {
        [subscriber sendNext:@1];
        [subscriber sendNext:@2];
        [subscriber sendNext:@3];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"signal dispose");
        }];
    }];

    RACSignal *bindSignal = [signal bind:^RACStreamBindBlock{
        return ^RACSignal *(NSNumber *value, BOOL *stop){
            value = @(value.integerValue * 2);
            return [RACSignal return:value];
        };
    }];

    [bindSignal subscribeNext:^(id x) {
        NSLog(@"subscribe value = %@", x);
    }];

由前第一节节详细讲解了RACSignal的创办和订阅的全经过,这个呢为艺术教,创建RACDynamicSignal,RACCompoundDisposable,RACPassthroughSubscriber这些都稍过,这里要分析一下bind的一一闭包传递创建与订阅的进程。

为防备接下的辨析会让读者看晕,这里先拿要就此到的block进行编号。

    RACSignal *signal = [RACSignal createSignal:
                         ^RACDisposable *(id<RACSubscriber> subscriber)
    {
        // block 1
    }

    RACSignal *bindSignal = [signal bind:^RACStreamBindBlock{
        // block 2
        return ^RACSignal *(NSNumber *value, BOOL *stop){
            // block 3
        };
    }];

    [bindSignal subscribeNext:^(id x) {
        // block 4
    }];

- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
        // block 5
    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        // block 6
        RACStreamBindBlock bindingBlock = block();
        NSMutableArray *signals = [NSMutableArray arrayWithObject:self];

        void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) {
        // block 7
        };

        void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
        // block 8
            RACDisposable *disposable = [signal subscribeNext:^(id x) {
            // block 9
            }];
        };

        @autoreleasepool {
            RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
                // block 10
                id signal = bindingBlock(x, &stop);

                @autoreleasepool {
                    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);
                }
            }];
        }
        return compoundDisposable;
    }] ;
}

优先创造信号signal,didSubscribe把block1 copy保存起来。

当信号调用bind进行绑定,会调用block5,didSubscribe把block6
copy保存起来。

当订阅者开始订阅bindSignal的早晚,流程如下:

  1. bindSignal执行didSubscribe的block,即执行block6。
  2. 于block6 的第一句代码,就是调用RACStreamBindBlock bindingBlock =
    block(),这里的block是外界传进的block2,于是起调用block2。执行完block2,会回去一个RACStreamBindBlock的对象。
  3. 由于是signal调用的bind函数,所以bind函数里面的self就是signal,在bind内部订阅了signal的信号。subscribeNext所以会履block1。
  4. 推行block1,sendNext调用订阅者subscriber的nextBlock,于是起施行block10。
  5. block10遭见面先调用bindingBlock,这个是前调用block2的回到值,这个RACStreamBindBlock对象中保存之是block3。所以开始调用block3。
  6. 以block3遇可参是一个value,这个value是signal中sendNext中作出去的value的值,在block3惨遭好对value进行换,变换完成后,返回一个新的信号signal’。
  7. 苟回到的signal’为空,则会调用completeSignal,即调用block7。block7中会发送sendCompleted。如果回去的signal’不呢空,则会调用addSignal,即调用block8。block8中会持续订阅signal’。执行block9。
  8. block9
    中见面sendNext,这里的subscriber是block6的入参,于是对subscriber调用sendNext,会调用到bindSignal的订阅者的block4中。
  9. block9
    中实践完sendNext,还见面调用sendCompleted。这里的凡以尽block9里面的completed闭包。completeSignal(signal,
    selfDisposable);然后以会调用completeSignal,即block7。
  10. 尽完block7,就形成了一致不成打signal 发送信号sendNext的咸经过。

bind整个工艺流程虽完事了。

  6、看起上面两段落代码来涉及,但是实际怎么打算的?

四. RACSignal基本操作concat和zipWith实现

接下还来分析RACSignal中另外2单基本操作。

深受咱们先来解决地方的困惑吧!

1. concat

betway体育 8

形容来测试代码:

    RACSignal *signal = [RACSignal createSignal:
                         ^RACDisposable *(id<RACSubscriber> subscriber)
    {
        [subscriber sendNext:@1];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"signal dispose");
        }];
    }];


    RACSignal *signals = [RACSignal createSignal:
                         ^RACDisposable *(id<RACSubscriber> subscriber)
    {
        [subscriber sendNext:@2];
        [subscriber sendNext:@3];
        [subscriber sendNext:@6];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"signal dispose");
        }];
    }];

    RACSignal *concatSignal = [signal concat:signals];

    [concatSignal subscribeNext:^(id x) {
        NSLog(@"subscribe value = %@", x);
    }];

concat操作就是管有限独信号合并起来。注意合并有先后顺序。

betway体育 9

- (RACSignal *)concat:(RACSignal *)signal {
   return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];

    RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
     // 发送第一个信号的值
     [subscriber sendNext:x];
    } error:^(NSError *error) {
     [subscriber sendError:error];
    } completed:^{
     // 订阅第二个信号
     RACDisposable *concattedDisposable = [signal subscribe:subscriber];
     serialDisposable.disposable = concattedDisposable;
  }];

    serialDisposable.disposable = sourceDisposable;
    return serialDisposable;
 }] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
}

集合前,signal和signals分别都拿各自的didSubscribe保存copy起来。
合并之后,合并之后新的信号的didSubscribe会把block保存copy起来。

当合并之后的信号为订阅的时段:

  1. 调用新的联合信号的didSubscribe。
  2. 由是首先个信号调用的concat方法,所以block中之self是前面一个信号signal。合并信号的didSubscribe会先订阅signal。
  3. 出于订阅了signal,于是开始实践signal的didSubscribe,sendNext,sendError。
  4. 现阶段一个信号signal发送sendCompleted之后,就会见开订阅后一个信号signals,调用signals的didSubscribe。
  5. 鉴于订阅了晚一个信号,于是后一个信号signals开始发送sendNext,sendError,sendCompleted。

然简单单信号就前后有序的拼凑到了一起。

此地发出好几内需专注的凡,两单信号concat在联名从此,新的信号的利落信号于其次个信号结束的早晚才收。看上图描述,新的信号的出殡长度等前面两独信号长度的同,concat之后的初信号的收尾信号吧即是次个信号的扫尾信号。

首先有些 订阅者和信号##\

  1、隐藏的订阅者

  平时咱们打交道的即使是信号,但是接连说订阅,却非明了订阅到底是何许开展的,也无力回天解答者的题材,让咱根据源码分析一下订阅过程。

  首先来认识一个目标:订阅者(RACSubscriber)。
订阅者订阅信号,就是这般简单的如出一辙桩业务。只不过框架隐藏了这目标,我们吧未必要跟订阅者打交道,只需要报信号一样宗工作,那即便是一旦发送了数据(三栽事件:next、complete、error),我们用举行什么工作(类似回调的概念)。

  第一步是创立信号,看一下点的首先段代码,createSignal类方法:
这里要说一下,信号RACSignal有有子类,我们常因此的是RACDynamicSignal和RACSubject,先不理会RACSubject。createSignal类方法创建的虽是RACDynamicSignal对象。

-----RACDynamicSignal.h-----
@property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);

-----RACSignal.m-----
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    return [RACDynamicSignal createSignal:didSubscribe];
}

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

  我们得发现,RACDynamicSignal有一个性能,名字叫didSubscribe的block对象。createSignal方法传递的block参数,就是赋值给didSubscribe属性。
  于问题1,我们可暂时这么对,createSignal的义是,创建一个signal对象,并且把参数赋值给signal的称也didSubscribe的特性,这个block的参数是subscriber,返回RACDisposable。

  第二步是订阅信号,看一下次截代码subscribeNext:

-----RACSubscriber.m-----
@property (nonatomic, copy) void (^next)(id value);

-----RACSignal.m-----
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);

    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}

-----RACDynamicSignal.m-----

- (RACDisposable *)subscribe:(id<RACSubscriber>)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;
}

  我们得以望,subscribeNext方法第一步是创办了一个RACSubscriber,也便是创立了一个订阅者,而且把subscribeNext的参数传递给RACSubscriber对象,RACSubscriber会把参数赋值给好一个名为吧next的Block类型的性,此地,我们得答应上面第4个问题,subscribeNext方法创建一个订阅者,并且将block参数,传递给订阅者一个名字叫next的习性,block参数接收的凡id类型,返回的是RACDisposable对象。连下去执行[self
subscribe:o],也便是订阅操作。我们在探望订阅方法subscribe的兑现:上面的代码很清楚,直接是self.didSubscribe(subscriber),我们得以理解,刚刚创建的subscriber对象,直接传送让上文中我们干的signal的didSubscribe属性。这般,我们可以解释上面的老二个同老三单问题,subscriber就是didSubscribe的形参,block对象是当subscribeNext的当儿实施之,刚刚的订阅者对象作为参数传入,就是subscriber对象。

  那么createSignal方法中,[subscriber sendNext:@(1)]大凡呀意思啊?
  看一下sendNext方法吧:

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

        nextBlock(value);
    }
}

  我们可以发现,sendNext的兑现,也即是直接执行及文中的nextBlock。也就算是应对了面第五独问题。

  总结一下,signal持有didSubscribe参数(createSignal传上的生block),subscriber持有nextBlock(就是subscribeNext传上的良block),当执行[signal
subscribe:subscriber]的时光,signal的didSubscribe执行,内部有subscriber
sendNext的调用,触发了subscriber的nextBlock的调用。到此,我们着力将信号和订阅者,以及订阅过程分析了。

2. zipWith

betway体育 10

形容来测试代码:

    RACSignal *concatSignal = [signal zipWith:signals];

    [concatSignal subscribeNext:^(id x) {
        NSLog(@"subscribe value = %@", x);
    }];

betway体育 11

源码如下:

- (RACSignal *)zipWith:(RACSignal *)signal {
    NSCParameterAssert(signal != nil);

    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        __block BOOL selfCompleted = NO;
        NSMutableArray *selfValues = [NSMutableArray array];

        __block BOOL otherCompleted = NO;
        NSMutableArray *otherValues = [NSMutableArray array];

        void (^sendCompletedIfNecessary)(void) = ^{
            @synchronized (selfValues) {
                BOOL selfEmpty = (selfCompleted && selfValues.count == 0);
                BOOL otherEmpty = (otherCompleted && otherValues.count == 0);

                // 如果任意一个信号完成并且数组里面空了,就整个信号算完成
                if (selfEmpty || otherEmpty) [subscriber sendCompleted];
            }
        };

        void (^sendNext)(void) = ^{
            @synchronized (selfValues) {

                // 数组里面的空了就返回。
                if (selfValues.count == 0) return;
                if (otherValues.count == 0) return;

                // 每次都取出两个数组里面的第0位的值,打包成元组
                RACTuple *tuple = RACTuplePack(selfValues[0], otherValues[0]);
                [selfValues removeObjectAtIndex:0];
                [otherValues removeObjectAtIndex:0];

                // 把元组发送出去
                [subscriber sendNext:tuple];
                sendCompletedIfNecessary();
            }
        };

        // 订阅第一个信号
        RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
            @synchronized (selfValues) {

                // 把第一个信号的值加入到数组中
                [selfValues addObject:x ?: RACTupleNil.tupleNil];
                sendNext();
            }
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            @synchronized (selfValues) {

                // 订阅完成时判断是否要发送完成信号
                selfCompleted = YES;
                sendCompletedIfNecessary();
            }
        }];

        // 订阅第二个信号
        RACDisposable *otherDisposable = [signal subscribeNext:^(id x) {
            @synchronized (selfValues) {

                // 把第二个信号加入到数组中
                [otherValues addObject:x ?: RACTupleNil.tupleNil];
                sendNext();
            }
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            @synchronized (selfValues) {

                // 订阅完成时判断是否要发送完成信号
                otherCompleted = YES;
                sendCompletedIfNecessary();
            }
        }];

        return [RACDisposable disposableWithBlock:^{

            // 销毁两个信号
            [selfDisposable dispose];
            [otherDisposable dispose];
        }];
    }] setNameWithFormat:@"[%@] -zipWith: %@", self.name, signal];
}

当把简单个信号通过zipWith之后,就像面的那张图一律,拉链的有限边给中间的拉索拉到了共。既然是拉链,那么一一底职位是来相应之,上面的拉链第一只位置只能对着下拉链第一独职务,这样牵连链才能够拉到一块儿错过。

现实贯彻:

zipWith里面有三三两两单数组,分别会晤蕴藏两独信号的值。

  1. 一经订阅了zipWith之后的信号,就从头执行didSubscribe闭包。
  2. 于闭包中会先行订阅第一独信号。这里要第一个信号于第二个信号先来一个价。第一只信号发出来的各个一个价值都见面为投入到第一单数组中保存起来,然后调用sendNext(
    )闭包。在sendNext(
    )闭包中,会先判断两个数组里面是否还为空,如果产生一个数组里面是空的,就return。由于第二个信号还未曾发送值,即第二只信号的数组里面凡是空的,所以这边首先个价值发送不出。于是第一只信号于订阅之后,发送的价存储到了第一单数组里面了,没有作下。
  3. 第二独信号的值紧接着有来了,第二单信号每发送一不善值,也会见储存到第二个数组中,但是是时候再调用sendNext(
    )闭包的早晚,不见面重return了,因为个别个数组里面都起价了,两只数组的第0声泪俱下位置还来一个价了。有价之后便从包成元组RACTuple发送出。并清空两独数组0声泪俱下位置存储的值。
  4. 后少个信号每次发送一个,就优先囤于屡次组吃,只要来“配对”的其它一个信号,就伙同打包成元组RACTuple发送出。从图中也堪见到,zipWith之后的初信号,每个信号的出殡时刻是当两只信号最晚发出信号的天天。
  5. 初信号的就时,是当双方任意一个信号就同时数组里面也空,就算完成了。所以最终第一只信号发送的5之老大值就深受废了。

先是个信号依次发送的1,2,3,4底值与亚只信号依次发送的A,B,C,D的价,一一底一道在了伙同,就比如拉链把他们拉扯在联名。由于5没法配对,所以拉链也牵涉非达了。

仲有的 信号和事件##\

  刚才我们说了,signal有几个子类,每一个种的signal订阅过程实际上大同小异,而且最初常见的吗就算是RACDynamicSignal,其实我们不需要极度关注这题目,因为不论是从定义信号,还是框架定义之一对category,例如,textFiled的rac_textSignal属性,大多数还是RACDynamicSignal。另一个周边的类别RACSubject可以下理解。

  还有即使是,我们恰好说到了三栽事件,分别是next、error、complete,和分析next的订阅过程一样,举个例,我们发送网络要,希望以失误的时刻,能叫用户提示,那么首先,创建信号的时光,在网络要失败的回调中,我们若[subscriber
sendError:netError],也就算是发送错误事件。然后于订阅错误事件,也尽管是subscriberError:…这样就算完事了不当信号的订阅。

  complete事件比突出,它来已订阅关系的意味,我们事先盖了解一下RACDispoable对象吧,我们明白,订阅关系要发出住的时,比如,在tableViewCell的复用的上,cell会订阅model类闹一个信号,但是当cell被复用的时候,如果非将前的订阅关系取消掉,就见面油然而生同时订阅了2单model的气象。我们可以窥见,subscribeNext、subscribeError、subscribeComplete事件返回的都是RACDisopable对象,当我们愿意停止订阅的时段,调用[RACDisposable
dispose]就可以了。complete也是者规律。

五. 最后

当就首稿子纪念将Map,combineLatest,flattenMap,flatten这些为一并分析了,但是后来来看RACSingnal的操作实际有些多,于是本源码的文本分别了,这里先拿RACSignal文件里的操作都分析了了。RACSignal文件之中的操作主要就bind,concat和zipWith三个操作。下一样首再分析分析RACSignal+Operations文件之中的保有操作。

恳请大家多多指教。

老三局部 进一步的尖锐##\

  RAC是一个雅庞大之框架,平时的有的课会误导大家纠结flattenMap和map的分别,这些题材,让人口摸不顶头绪,导致入门更加的不方便。实际上,学习她要一个循循渐进的过程,RAC有多意图,解耦合,更高速之解决一好像题目等等,总之,他是指向正规的面向对象编程很好的增补。所以,在明亮了订阅的经过之后,重要之是,投入其实的用中,我观察了森开源的类,并整合自己之履发现,其实flattenMap这样的操作,非常少,几乎从未,常用之就就是是以下几单:手动构建信号(createSignal)、订阅信号(subscribeNext)、使用框架的片段宏定义(RACObserve、RAC)、然后就是是上几单极度简便的操作,例如map、merge等就是好初步了。如果期待深入钻研,一定要是将这些基础之物吃透,然后于上还多之操作,例如flattenMap,了解side
effect和多播的概念,学会RACSubject的用法(这个邪是格外关键之目标)等等。如果将这些操作比作武器的话,可能再着重之是内功,也就是是知道他的思维,我们怎么通过实战,知道确切的动他的雄强,慢慢的熟悉与深入是历届至渠道成的工作。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图