RACScheduler

文章系列
《ReactiveCocoa 概述》
《RACSignal》
《RACDisposable》
《RACSubject践惑、RACReplaySubject(內(nèi)附冷信號和熱信號的區(qū)別)》
《集合RACTuple馅巷、RACSequence》
《RAC 中的通知镐作、代理、KVO, 基本事件扛禽、方法的監(jiān)聽》
《rac_liftSelector》
《RACMulticastConnection》
《RACCommand》
《RAC - 核心方法bind》
《RAC - 定時器》
《RACScheduler》
《RAC - 點擊獲取驗證碼 demo》
《RAC - 映射(Map & flattenMap)》
《RAC信號操作解釋合集》
《RAC - 信號的生命周期》

首先先理解四個名詞的概念, 便于后續(xù)的理解.

  • 同步: 順序執(zhí)行,執(zhí)行完一個再執(zhí)行下一個,需要等待特姐、協(xié)調(diào)運行.
  • 異步: 是彼此獨立,在等待某事件的過程中繼續(xù)做自己的事,不需要等待這一事件完成后再工作.
  • 并發(fā): 單核cpu 同時調(diào)度不同的線程.
  • 并行: 針對多核處理器而言, 進程不僅可以交替執(zhí)行,而且可以重疊執(zhí)行, 即每一核單獨處理單獨的線程.

1. RACScheduler 的定義

通俗來講, RACScheduler 就是iOS 開發(fā)中的多線程, 且底層是用GCD封裝的.

2. RACScheduler 的類方法

  1. currentScheduler: 獲取當(dāng)前線程調(diào)度器.
    在線程字典中根據(jù)@"RACSchedulerCurrentSchedulerKey"這個key 來取出對應(yīng)的RACScheduler.
    如果是在主線程上,就返回mainThreadScheduler.
    如果既不在主線程上有序,線程字典里面也找不到對應(yīng)key值對應(yīng)的value,那么就返回nil.
NSLog(@"當(dāng)前線程: %@", [RACScheduler currentScheduler]);

打印結(jié)果: 當(dāng)前線程: <RACTargetQueueScheduler: 0x6000015420a0> org.reactivecocoa.ReactiveObjC.RACScheduler.mainThreadScheduler

  1. mainThreadScheduler: 獲取主線程調(diào)度器.
注:單例
    [[RACScheduler mainThreadScheduler] schedule:^{
        
        NSLog(@"當(dāng)前線程: %@", [RACScheduler currentScheduler]);
    }];

打印結(jié)果:當(dāng)前線程: <RACTargetQueueScheduler: 0x600003a9d2c0> org.reactivecocoa.ReactiveObjC.RACScheduler.mainThreadScheduler

  1. scheduler: 這是一個異步線程岛请,不會對主線程造成堵塞旭寿,異步執(zhí)行.
注: 非單例, 每調(diào)用一次, 都會生成不同的scheduler 對象, 互不相同, 互不干擾.
    [[RACScheduler scheduler] schedule:^{
        
        NSLog(@"當(dāng)前線程: %@", [RACScheduler currentScheduler]);
    }];

打印結(jié)果: 當(dāng)前線程: <RACTargetQueueScheduler: 0x600001555ec0> org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler

  1. immediateScheduler ,立即執(zhí)行的線程崇败,其實就是在主線程執(zhí)行的.
注:單例
    [[RACScheduler immediateScheduler] schedule:^{
        
        NSLog(@"當(dāng)前線程: %@", [RACScheduler currentScheduler]); 
    }];

打印結(jié)果: 當(dāng)前線程: <RACTargetQueueScheduler: 0x6000037cdbc0> org.reactivecocoa.ReactiveObjC.RACScheduler.mainThreadScheduler

注: 慎用, immediateScheduler 作用就是在當(dāng)前線程立即執(zhí)行當(dāng)前給的代碼塊內(nèi)容, 容易造成死鎖!!!

3. RACScheduler 的優(yōu)先級設(shè)置

首先對類方法scheduler做一個補充

+ (RACScheduler *)scheduler;
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority;
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name;

↓三個方法的本質(zhì)是相同的, 點擊看下實現(xiàn), 就明白了↓

// 創(chuàng)建出來的queue的優(yōu)先級是默認(rèn)的盅称,名字也是默認(rèn)的
+ (instancetype)scheduler {
    return [self schedulerWithPriority:RACSchedulerPriorityDefault];
}

// 方法只能執(zhí)行優(yōu)先級,名字為默認(rèn)的
+ (instancetype)schedulerWithPriority:(RACSchedulerPriority)priority {
    return [self schedulerWithPriority:priority name:@"com.ReactiveCocoa.RACScheduler.backgroundScheduler"];
}

// 方法可以指定線程的優(yōu)先級和名字
+ (instancetype)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name {
    return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)];
}

RACScheduler中的優(yōu)先級后室,這里只封裝了4種

typedef enum : long {
     RACSchedulerPriorityHigh = DISPATCH_QUEUE_PRIORITY_HIGH,
     RACSchedulerPriorityDefault = DISPATCH_QUEUE_PRIORITY_DEFAULT,
     RACSchedulerPriorityLow = DISPATCH_QUEUE_PRIORITY_LOW,
     RACSchedulerPriorityBackground = DISPATCH_QUEUE_PRIORITY_BACKGROUND,
} RACSchedulerPriority;

示例代碼
如何指定某個線程的優(yōu)先級??? 使用方式3schedulerWithPriority: name:來設(shè)置, 即指定了線程, 也自定義了名稱`

    [[RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh] schedule:^{
    
        NSLog(@"當(dāng)前線程: %@", [RACScheduler currentScheduler]);
    }];


    [[RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh name:@"指定線程"] schedule:^{
        
        NSLog(@"指定線程: %@",[RACScheduler currentScheduler]);
    }];

打印結(jié)果:
RAC 線程優(yōu)先級設(shè)置

4. RACScheduler 常用的對象方法

注: schedule方法可以使用dispose 來取消任務(wù), 本質(zhì)是偽取消, 只是沒有回調(diào)內(nèi)部封裝的block.

// 是為RACScheduler添加一個任務(wù)微渠,入?yún)⑹且粋€閉包
- (RACDisposable *)schedule:(void (^)(void))block;

// 是為RACScheduler添加一個定時任務(wù),在date時間之后才執(zhí)行任務(wù)
- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block;

// 是為RACScheduler添加一個延時執(zhí)行的任務(wù)咧擂,延時delay時間之后才執(zhí)行任務(wù)
- (RACDisposable *)afterDelay:(NSTimeInterval)delay schedule:(void (^)(void))block;

// 是為RACScheduler添加一個定時任務(wù),在date時間之后才開始執(zhí)行檀蹋,然后每隔interval秒執(zhí)行一次任務(wù)
- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block;

注:一個Scheduler 保證串行執(zhí)行; 但一個Scheduler 的任務(wù)不保證線程是同一個.

5.當(dāng)RACSignal 遇到了RACScheduler

接下來分析當(dāng)RACSignal 遇到了RACScheduler 時, 信號的發(fā)送和接收在不同線程中的訂閱情況

RACSignal 訂閱順序回顧
RACSignal 訂閱順序
子線程訂閱順序分析

結(jié)論: 子線程訂閱信號, 意味著整個訂閱過程將在子線程中執(zhí)行.

子線程訂閱
線程打印圖

信號異步發(fā)送

結(jié)論: 信號異步發(fā)送, subscribeNext 的這個閉包, 一定在信號發(fā)送的線程執(zhí)行.

信號異步發(fā)送
333 在子線程中打印

信號在不同的scheduler 中發(fā)送

subscribeNext: 的執(zhí)行一定在對應(yīng)的sendNext: 的線程中執(zhí)行.

信號在不同的scheduler 中發(fā)送
1 在主線程中打印; 1.1在子線程中打印

訂閱和發(fā)送都在不同的scheduler 中

didSubscribeblock 中的內(nèi)容, 一定跟隨訂閱著subscriber 同線程中執(zhí)行.
subscribeNext block 中的內(nèi)容, 一定跟隨信號發(fā)送sendNext同線程執(zhí)行.

訂閱和發(fā)送都在不同的scheduler 中
訂閱和發(fā)送都在不同的scheduler 中線程打印

發(fā)現(xiàn)問題 ???
  • 訂閱時機不確定
  • 發(fā)送時機也不確定

所以, 得到的結(jié)果就不確定, 那怎么解決呢? 采用以下兩種方式來處理:

  • 訂閱時機不確定 -> subscribeOn:
  • 發(fā)送時機也不確定 -> deliveOn
subscribeOn

負(fù)責(zé)使當(dāng)前訂閱者訂閱后立即執(zhí)行的didSubscribe block 執(zhí)行在指定線程中.

  • 84行使用subscribeOn操作后, 直接使(67~79)行的didSubscribe block 在84行指定的mainThreadScheduler中執(zhí)行. 對應(yīng)線程圖中子線程中subscriber -> 主線程中didSubscribe 的過程(灰色弧形箭頭部分).
    subscribeOn 示例

    subscribeOn 操作后的訂閱順序

subscribeOn 總結(jié):

  • 能夠保證didSubscribe block 在指定的scheduler 中執(zhí)行.
  • 不能保證sendNext松申、senfError云芦、sendComplete 在那個scheduler 中執(zhí)行.
deliveOn

能夠保證subscribeNext:在指定線程中執(zhí)行.

  • 下面的示例中, 56行的subscribeNext: 都是在53行指定的mainThreadScheduler主線程中執(zhí)行的.
    deliveOn 示例
    deliveOn 操作后的訂閱順序

.End

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市贸桶,隨后出現(xiàn)的幾起案子舅逸,更是在濱河造成了極大的恐慌,老刑警劉巖皇筛,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琉历,死亡現(xiàn)場離奇詭異,居然都是意外死亡水醋,警方通過查閱死者的電腦和手機旗笔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拄踪,“玉大人蝇恶,你說我怎么就攤上這事』掏” “怎么了撮弧?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長姚糊。 經(jīng)常有香客問我贿衍,道長,這世上最難降的妖魔是什么救恨? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任贸辈,我火速辦了婚禮,結(jié)果婚禮上忿薇,老公的妹妹穿的比我還像新娘裙椭。我一直安慰自己曹铃,他們只是感情好加叁,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著约急,像睡著了一般筋栋。 火紅的嫁衣襯著肌膚如雪炊汤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天弊攘,我揣著相機與錄音抢腐,去河邊找鬼。 笑死襟交,一個胖子當(dāng)著我的面吹牛迈倍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捣域,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼啼染,長吁一口氣:“原來是場噩夢啊……” “哼宴合!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起迹鹅,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤卦洽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后斜棚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阀蒂,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年弟蚀,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚤霞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡粗梭,死狀恐怖争便,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情断医,我是刑警寧澤滞乙,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站鉴嗤,受9級特大地震影響斩启,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜醉锅,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一兔簇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硬耍,春花似錦垄琐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至坯认,卻和暖如春翻擒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背牛哺。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工陋气, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人引润。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓巩趁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親淳附。 傳聞我的和親對象是個殘疾皇子晶渠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355