iOS RAC的实现原理3-RACSubject

RACSubject 信号提供者!!,自己可以充当信号,又能够发送信号!!

首先回顾一下

     RACDisposable:它可以帮助我们取消订阅.信号发送完毕了 ,失败了.
     
     RACSubscriber(协议):订阅者(发送信号!)
     
     RACSubject :信号提供者!!,自己可以充当信号,又能够发送信号!!
     

RACSubject:这个类叫做信号提供者,自己可以充当信号,又能够发送信号!!

@interface RACSubject<ValueType> : RACSignal<ValueType> <RACSubscriber>
编程思想:《面向协议的开发》
     
OC里边没有多继承这一说,那么我(RACSubject)想继承另一个类(RACSignal)里的功能:
就需要 我(RACSubject) 遵守 订阅者协议< RACSubscriber >,实现订阅者协议的方法,就可以了。
     
就是面向协议的开发的应用场景。

还是三步走:

1.创建信号
2.订阅信号
3.发送数据
//1.创建信号
    RACSubject *subject = [RACSubject subject];
    
    //2.订阅信号
    //不同的信号订阅的方式不一样!!因为类型不一样,所以调用的方法不一样。
    //RACSubject处理订阅 :拿到之前的_subscribers 保存订阅者
    [subject subscribeNext:^(id  _Nullable x) {
      
        NSLog(@"接收到的数据 x 是 %@",x);
        
    }];
    
    
    
    //3.发送数据
    //遍历出所有的订阅者,其实还是调用的nextBlock
    [subject sendNext:@"数据A"];

打印的结果:

2017-06-15 16:18:54.468 RAC-demo[41104:6465743] 接收到的数据 x 是 数据A

多订阅者

//1.创建信号
    RACSubject *subject = [RACSubject subject];
    
    //2.订阅信号
    //不同的信号订阅的方式不一样!!因为类型不一样,所以调用的方法不一样。
    //RACSubject处理订阅 :拿到之前的_subscribers 保存订阅者
    [subject subscribeNext:^(id  _Nullable x) {
      
        NSLog(@"订阅1⃣️ 接收到的数据 x 是 %@",x);
        
    }];
    [subject subscribeNext:^(id  _Nullable x) {
        
        NSLog(@"订阅2⃣️ 接收到的数据 x 是 %@",x);
        
    }];
    
  
    //3.发送数据
    //遍历出所有的订阅者,其实还是调用的nextBlock
    [subject sendNext:@"数据A"];

打印的结果:

2017-06-15 17:04:56.804 RAC-demo[41379:6621767] 订阅1⃣️ 接收到的数据 x 是 数据A
2017-06-15 17:04:56.805 RAC-demo[41379:6621767] 订阅2⃣️ 接收到的数据 x 是 数据A

实现原理

1、创建信号
订阅管理者(_disposable)、保存订阅者的数组(_subscribers)

" cmd "点"subject"进去
" [RACSubject subject] "

// 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;

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

- (void)dealloc {
    [self.disposable dispose];
}
作者在重写的init方法里面进行了创建信号订阅管理者(_disposable)、保存订阅者的数组(_subscribers),
便于多个订阅者订阅
"_disposable"、"_subscribers"

2、订阅信号
RACSubject处理订阅 :拿到之前的_subscribers 保存订阅者

"cmd"点"subscribeNext "进去
" [subject subscribeNext:^(id  _Nullable x) {}] "

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

保存Block
nextBlock

注意" [self subscribe:o] "
"cmd"点"subscribe "进去
此处的"self"代表的是"RACSubject"

- (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) {
        [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<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
                return obj == subscriber;
            }];

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

    return disposable;
}

保存所有订阅者
[subscribers addObject:subscriber];

#@synchronized上锁的原因是:@synchronized() 的作用是:
#创建一个互斥锁,保证在同一时间内没有其它线程对self对象进行修改,起到线程的保护作用,
#一般在公用变量的时候使用,如单例模式或者操作类的static变量中使用。

这里把订阅管理者disposable返回出去便于 手动 取消订阅

3、发送数据
遍历出所有的订阅者,其实还是调用的nextBlock

"cmd"点"sendNext"进去
"[subject sendNext:@"数据A"]"

- (void)sendNext:(id)value {
    [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
        [subscriber sendNext:value];
    }];
}
看到"enumerateSubscribersUsingBlock"就知道这是个循环,
就是要把之前保存的订阅者一个一个找出发送信号
#下边这个方法证实了这个的想法:
- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
    NSArray *subscribers;
    @synchronized (self.subscribers) {
        subscribers = [self.subscribers copy];
    }

    for (id<RACSubscriber> subscriber in subscribers) {
        block(subscriber);
    }
}
#block(subscriber);
block一调用就近到了"[subscriber sendNext:value];"方法
#pragma mark RACSubscriber

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

        nextBlock(value);
    }
}
#看到了熟悉的
nextBlock(value);
这就表示在这里进行了发送数据把保存的nextBlock一执行就实现了数据返回后的处理了。

流程图示:


实现原理图示.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • RAC使用测试Demo下载:github.com/FuWees/WPRACTestDemo 1.ReactiveC...
    FuWees阅读 6,355评论 3 10
  • 标签: iOS RAC 概述 ReactiveCocoa是一个函数响应式编程框架,它能让我们脱离Cocoa AP...
    GodyZ阅读 7,509评论 16 97
  • 前言 之前对RAC有了一个基本的认识,了解了它的作用,以及RAC的运行机制,我们知道只要是信号(RACSignal...
    大大盆子阅读 4,495评论 0 11
  • 1.ReactiveCocoa简介 ReactiveCocoa(简称为RAC),是由Github开源的一个应用于i...
    爱睡觉的魚阅读 1,142评论 0 1
  • 从a页面跳转到b页面,从b页面返回到a页面时向a页面传递一个参数,a页面接收到参数后做出响应a.html b.ht...
    红叶丶秋鸣阅读 2,337评论 1 1