ReactiveCocoa 使用.RACSignal(信号源)类使用

RACSignal 是RAC的核心,几乎所有的操作都是围绕着RACSignal类在执行的。这一章会讲解RACSignal类的创建和使用。如果读者对ReactiveCocoa的一些名词和架构不熟悉可以看上一篇ReactiveCocoa 基础.架构介绍

完成一个信号的生命周期大体分为四步

  • 创建信号
  • 订阅信号
  • 发送信号
  • 取消订阅
屏幕快照 2017-08-31 下午3.33.26.png

代码范例

  1 创建Signal
  RACSignal *testSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            // 3 发送信号
            [subscriber sendNext:@"土豆萝卜君"];
            [subscriber sendCompleted];
            
            return [RACDisposable disposableWithBlock:^{
                //4 用于取消订阅清理资源 一般用来释放一些变量和对象 如果没有的话此处可以为nil
            }];
        }];

  2订阅Next信号
    [testSignal subscribeNext:^(id x) {
            NSLog(@"%@",x);
    }];

内部解析

1 创建信号

1.1 由上面的信号类使用图和代码可知,创建信号类方法中传入了一个返回值是RACDisposable 类型的实例变量,参数为id类型且遵守且遵守RACSubscriber协议的subscriber订阅者,名为didSubscribe的block 代码如下

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

1.2 类创建的是一个 RACDynamicSignal 类型的动态信号,并将 didSubscribe 传入

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];  //重要方法 保存上一个传入的didSubscribe block  
    return [signal setNameWithFormat:@"+createSignal:"];
}

创建了一个 RACDynamicSignal 类型的信号,然后将传入的名为 didSubscribe 的block保存在创建的信号的 didSubscribe 属性中保存但是并未触发 (在有订阅者订阅此信号时候执行,此时只是一个冷信号)

2 订阅信号

订阅信号有subscribeNext, subscribeError, subscribeComlepted 三种方法。当信号被 订阅的时候此时冷信号变成了热信号就可以执行发送信号的操作。

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);  //如果nextBlock为空不在执行
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
  //内部创建了RACSubscriber(订阅者)类的实例对象o,并且将nextBlock保存到o中,在返回值出执行o,实际也是执行了nextBlock。
    return [self subscribe:o];
}

2.1 上面代码创建订阅者的实质是,创建一个订阅者,并保存相应的block 此时是保存并未触发![self subscribe:o] 方法才是最重要的 (RACDynamicSignal类中)

- (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);
             //在这里执行了在步骤1传入的 didSubscribe block 并返回一个RACDisposable类型的实例变量

            [disposable addDisposable:innerDisposable];
        }];

        [disposable addDisposable:schedulingDisposable];
    }
    
    return disposable;
}
  • 1 代码中传入了2.1中创建的RACSubscriber *o 订阅者,里面保存着 步骤1创建信号时的didSubscribe block

  • 2 这里生成了一个RACCompoundDisposable类型的disposable 主要用来管理订阅结束以及资源的清理

  • 3 RACPassthroughSubscriber 类型的订阅者主要是对传入的订阅者,当前的信号以及创建的disposable 进行一个包装,感觉作用就是做一个变量的统一入口,实际起作用的应该还是刚传入的订阅者

  • 4 执行 didSubscribe block 将返回过来的innerDisposable 传入刚刚生成的disposable,也把调度器返回过来的schedulingDisposable 也保存到disposable 然后统一管理。当 RACCompoundDisposable 对象被 disposed 时,它会调用其所包含的所有 disposable 对象的 -dispose 方法从而做到统一管理

  • 5 总结订阅信号本质就是创建了一个 RACPassthroughSubscriber 类型的订阅者,并将传入的代码块保存起来,留待以后调用,同时调用了第一步创建信号中保存的代码块,并传入创建的订阅者

3 发送信号
- (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];
}
  • 发送信号就是执行订阅时候传入的next error completed的block

  • 对于 sendError 和 sendCompleted 都是先取消订阅,再执行相应的代码块,而 sendNext 并未使订阅结束,这样的话,对之后讨论的各种组合方法中必须写上 sendCompleted来结束订阅的做法就好理解了也就印证了上一篇的 "一个信号的生命周期是由任意个 next 事件和一个 error 事件或一个 completed 事件组成的"这句话。

4取消订阅

想要结束订阅只要将相应生成的disposable执行dispose即可。原因在于步骤2中的调度器

   RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
           RACDisposable *innerDisposable = self.didSubscribe(subscriber);
           [disposable addDisposable:innerDisposable];
       }];
  • 首先我们要知道执行订阅的代码块实际上是放到相应的调度器中去执行,接下来点击方法中
- (RACDisposable *)schedule:(void (^)(void))block {
   NSCParameterAssert(block != NULL);

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

   block();
   return nil;
}

  • 如果currentScheduler不为nil的时候代码块就不会放到调度器里去执行,而是直接执行,此时disposable的dispose方法就没有用了

  • 如果currentScheduler为空的时候会执行

- (RACDisposable *)schedule:(void (^)(void))block {
    NSCParameterAssert(block != NULL);

    RACDisposable *disposable = [[RACDisposable alloc] init];

    dispatch_async(self.queue, ^{
        if (disposable.disposed) return;
        [self performAsCurrentScheduler:block];
    });
    return disposable;
}

  • 代码块未被调度的之前,生成的disposable被dispose的话,代码块就不会被执行。

总结:

  • 步骤1 创建信号本质就是创建了一个 RACDynamicSignal类型的动态信号,并将传入block保存起来
  • 步骤2 订阅信号本质就是创建了一个 RACPassthroughSubscriber 类型的订阅者,并将传入的nextBlock保存起来同时调用了第一步创建信号中保存的代码块,并传入创建的订阅者
  • 步骤3 发送信号就是执行订阅信号时对应的block。
  • 步骤4 取消订阅就是把订阅信号获得的disposable 进行dispose即可在调度器调度该部分之前直接返回不在继续执行达到取消的效果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容