iOS ReactiveObjc框架解析

簡介

ReactiveObjc將原有的各種設(shè)計模式讥蟆,包括代理、Target/Action停撞、block、通知中心以及觀察者模式各種『輸入』,都抽象成了數(shù)據(jù)流或者信號(也可以理解為狀態(tài)流)讓單一的組件能夠?qū)ψ约旱捻憫?yīng)動作進(jìn)行控制戈毒,簡化了視圖控制器的負(fù)擔(dān)艰猬。ReactiveObjc整個框架主要是圍繞Signal來運轉(zhuǎn)。
我們先來看一段代碼

RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@"2"];
        [subscriber sendNext:@"3"];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"釋放");
        }];
    }];
    
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];

這段代碼的打印

2021-04-12 15:15:31.988297+0800 ZLXStudyDemo[64724:7390883] 1
2021-04-12 15:15:31.988485+0800 ZLXStudyDemo[64724:7390883] 2
2021-04-12 15:15:31.988587+0800 ZLXStudyDemo[64724:7390883] 3
2021-04-12 15:15:34.694916+0800 ZLXStudyDemo[64724:7390883] 釋放

這段代碼包含ReactiveObjc的信號完成使用邏輯埋市。
我們先大致分析下:通過RACSignal的createSignal:創(chuàng)建一個signal->signal通過subscribeNext:訂閱一個信號-> RACSubscriber通過sendNext來觸發(fā)訂閱的信號冠桃。

下面我們從以下四方來解析上述過程的原理:

  • 信號的創(chuàng)建
  • 信號的訂閱
  • 訂閱的響應(yīng)
  • 訂閱的回收

信號的創(chuàng)建

RACSignal是ReactiveObjc框架信號處理的核心。信號的創(chuàng)建我們主要來看下這個類道宅。

先看下創(chuàng)建相關(guān)的代碼:

// step1
 RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@"2"];
        [subscriber sendNext:@"3"];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"釋放");
        }];
    }];

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

信號的創(chuàng)建是不是非常簡單腊满。

  • RACSignal調(diào)用create內(nèi)部再調(diào)用RACDynamicSignal的create
  • RACDynamicSignal將傳入Subscribeblock綁定到RACDynamicSignal的didSubscribe屬性上。


    image.png

所以整個信號的訂閱我們可以理解為:綁定信號關(guān)聯(lián)的block到RACDynamicSignal的didSubscribe屬性上培己。
這個關(guān)聯(lián)的block什么時候觸發(fā)呢? 怎么觸發(fā)的呢胚泌? 接下來我們來看下信號的訂閱省咨。

信號的訂閱

ReactiveObjc訂閱的相關(guān)邏輯我們主要看RACSubscriber

訂閱相關(guān)的代碼:

// step1
[signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
 }];

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

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

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

我們來分析下訂閱的過程:

  • RACSignal調(diào)用訂閱API,傳入訂閱的block
  • 創(chuàng)建RACSubscriber,并綁定上一步的block
  • RACSignal調(diào)用subscribe玷室,傳入上一步創(chuàng)建的RACSubscriber零蓉,
  • 調(diào)用之前RACSignal信號創(chuàng)建時候綁定的didSubscribe,并傳入上一步傳入的RACSubscriber

讓我們在看看看剛開始的代碼穷缤,現(xiàn)在知道[subscriber sendNext:@"1"];這個subscriber是什么時候創(chuàng)建的了嗎敌蜂?上面的步驟2

RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
// 這個subscriber什么時候創(chuàng)建的呢?
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@"2"];
        [subscriber sendNext:@"3"];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"釋放");
        }];
    }];
image.png

訂閱的響應(yīng)

//step1 調(diào)用sendNext
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@"2"];
        [subscriber sendNext:@"3"];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"釋放");
        }];
    }];
//step2 調(diào)用nextBlock
- (void)sendNext:(id)value {
    @synchronized (self) {
        void (^nextBlock)(id) = [self.next copy];
        if (nextBlock == nil) return;

        nextBlock(value);
    }
}
// 響應(yīng)block
[signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x); 
}];

這個流程是不是很簡單津肛。給你個問題 nextblock哪里來的章喉?

訂閱的取消

RACDisposable 是訂閱回收的核心。主要思想是用來處理異步的操作身坐。
每一個異步操作關(guān)聯(lián)一個RACDisposable秸脱,當(dāng)執(zhí)行操作的時候,去check RACDisposable的狀態(tài)部蛇。

我們來看下之前的訂閱代碼

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);
//step1 創(chuàng)建一個容器disposable摊唇,可以同時處理多個disposable
    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
//消息轉(zhuǎn)發(fā)。主要作用是check disposable狀態(tài)
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

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

我們來看下代碼吧涯鲁,每次執(zhí)行操作的時候都去check下disposed巷查。

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

// RACSubscriber 釋放調(diào)用掛載的RACDisposable的block
- (void)dealloc {
    [self.disposable dispose];
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抹腿,隨后出現(xiàn)的幾起案子岛请,更是在濱河造成了極大的恐慌,老刑警劉巖幢踏,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件髓需,死亡現(xiàn)場離奇詭異,居然都是意外死亡房蝉,警方通過查閱死者的電腦和手機僚匆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門微渠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人咧擂,你說我怎么就攤上這事逞盆。” “怎么了松申?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵云芦,是天一觀的道長。 經(jīng)常有香客問我贸桶,道長舅逸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任皇筛,我火速辦了婚禮琉历,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘水醋。我一直安慰自己旗笔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布拄踪。 她就那樣靜靜地躺著蝇恶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惶桐。 梳的紋絲不亂的頭發(fā)上撮弧,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機與錄音姚糊,去河邊找鬼想虎。 笑死,一個胖子當(dāng)著我的面吹牛叛拷,可吹牛的內(nèi)容都是我干的舌厨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼忿薇,長吁一口氣:“原來是場噩夢啊……” “哼裙椭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起署浩,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤揉燃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后筋栋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炊汤,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了抢腐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姑曙。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖迈倍,靈堂內(nèi)的尸體忽然破棺而出伤靠,到底是詐尸還是另有隱情,我是刑警寧澤啼染,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布宴合,位于F島的核電站,受9級特大地震影響迹鹅,放射性物質(zhì)發(fā)生泄漏卦洽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一斜棚、第九天 我趴在偏房一處隱蔽的房頂上張望逐样。 院中可真熱鬧,春花似錦打肝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至级零,卻和暖如春断医,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奏纪。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工鉴嗤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人序调。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓醉锅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親发绢。 傳聞我的和親對象是個殘疾皇子硬耍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容