文章系列
《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 的類方法
-
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
-
mainThreadScheduler
: 獲取主線程調(diào)度器.
注:單例
[[RACScheduler mainThreadScheduler] schedule:^{
NSLog(@"當(dāng)前線程: %@", [RACScheduler currentScheduler]);
}];
打印結(jié)果:當(dāng)前線程: <RACTargetQueueScheduler: 0x600003a9d2c0> org.reactivecocoa.ReactiveObjC.RACScheduler.mainThreadScheduler
-
scheduler
: 這是一個異步線程岛请,不會對主線程造成堵塞旭寿,異步執(zhí)行.
注: 非單例, 每調(diào)用一次, 都會生成不同的scheduler 對象, 互不相同, 互不干擾.
[[RACScheduler scheduler] schedule:^{
NSLog(@"當(dāng)前線程: %@", [RACScheduler currentScheduler]);
}];
打印結(jié)果: 當(dāng)前線程: <RACTargetQueueScheduler: 0x600001555ec0> org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler
- 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 中
didSubscribe
block 中的內(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