iOS 子線程用runloop保活的一個方案

在我們需要頻繁地開啟子線程執(zhí)行操作的時候芜果,我們可以采用開啟子線程runloop的方式本铣剩活子線程,這樣避免頻繁創(chuàng)建線程銷毀線程的開銷右钾。
具體的币狭撸活方式.

@interface ZLPermanentThread : NSObject
/** 在當前子線程執(zhí)行一個任務(wù)*/
- (void)executeTask:(void(^)(void))task;
/**結(jié)束線程*/
- (void)stop;
@end

@interface ZLPermanentThread()
@property (nonatomic, strong) NSThread *thread;
//@property (nonatomic, assign, getter=isStopped) BOOL stopped;
@end

@implementation ZLPermanentThread

- (instancetype)init{
    if (self = [super init]) {
//        self.stopped = NO;
//        __weak typeof(self) weakSelf = self;
        self.thread = [[NSThread alloc] initWithBlock:^{// ios 10之后才有這個方法
            
            CFRunLoopSourceContext context = {0};
            CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
            CFRelease(source);
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);

            NSLog(@"runloop end---");
//            NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
//            [currentRunloop addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
//            while (weakSelf && !weakSelf.isStopped) {
//                [currentRunloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//            }
        }];
        [self.thread start];
    }
    return self;
}

- (void)__executeTask:(void (^)(void))task{
    task();
}

- (void)executeTask:(void (^)(void))task{
    if (!self.thread || !task) return;
    
    [self performSelector:@selector(__executeTask:) onThread:self.thread withObject:task waitUntilDone:NO];
}

- (void)stop{
    if (!self.thread) return;
    
    [self performSelector:@selector(__stop) onThread:self.thread withObject:nil waitUntilDone:YES];
}

- (void)__stop{
//    self.stopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.thread = nil;
}

- (void)dealloc{
    NSLog(@"%s", __func__);
    [self stop];
}

@end

使用時

// 初始化
self.thread = [[ZLPermanentThread alloc] init];
// 執(zhí)行任務(wù)
[self.thread executeTask:^{
    NSLog(@"子線程執(zhí)行了任務(wù)");
}];

關(guān)鍵點:

  1. 子線程的runloop需要手動去調(diào)用運行。
  2. 子線程runloop手動運行成功開啟循環(huán)需要先在runloop中添加事件源或者timer舀射。
  3. CFRunLoopStop(CFRunLoopGetCurrent());可以停止runloop運行窘茁。
附:Runloop相關(guān)知識點
1. Runloop與線程的關(guān)系尘分?

Runloop是一個運行循環(huán),與線程是一一對應(yīng)的關(guān)系描滔,線程中有runloop或者自己定義的循環(huán)運行時笙僚,子線程就不會被銷毀,主線程中Runloop是默認開啟的慎王;runloop是系統(tǒng)提供的運行循環(huán),相比自己手動寫while循環(huán),有以下幾個優(yōu)點:

  1. 能處理APP的input souce事件张抄、定時器事件、代碼執(zhí)行的事件洼怔。
  2. 在沒有事件要處理時會進入休眠(系統(tǒng)層面的休眠函數(shù))署惯,節(jié)省CPU資源。

下面的runloop的本質(zhì)結(jié)構(gòu):

// 下載最大版本號的源碼壓縮包镣隶,找到__CFRunLoop.
// 下面是簡化了一些變量后的__CFRunLoop
struct __CFRunLoop {
    pthread_t _pthread;
    CFMutableSetRef _commonModes; // 共同的模式极谊,相當于NSRunLoopCommonModes
    CFMutableSetRef _commonModeItems;// 共同的模式下的Item
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
};
// 下面是簡化了一些變量后的__CFRunLoopMode
struct __CFRunLoopMode {
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
};

有幾件事情需要說明一下:

1.runloop同一時間只能處于一個RunLoopMode下.

  1. 系統(tǒng)提供每個RunLoopMode下會有自己的Item,這個item可能是source,timer,observer; 自定義的RunLoopMode安岂,自定義的RunLoopMode需要自己往里面添加Item轻猖。
  2. 如果一個runloop中沒有任何一個item,那么runloop會立即停止域那。
2. Runloop有幾種模式咙边?

官網(wǎng)顯示的是5種,我們需要關(guān)心的就是:Default模式、tracking模式败许、Common模式王带。NSRunloop簡單細說(六)—— 幾種循環(huán)模式詳細解析

3. Runloop有幾種狀態(tài)?

7種狀態(tài):即將進入市殷、即將處理timer愕撰、即將處理source、即將進入休眠醋寝、從休眠中喚醒盟戏、退出runloop、AllActivities甥桂。
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};

4. Runloop是怎么運行的

Runloop運行的時候是開啟了一個do..while循環(huán)柿究,執(zhí)行一個循環(huán)時首先處理完Observer、Source0黄选、Source1蝇摸、timer,然后調(diào)用系統(tǒng)的休眠函數(shù)等待被喚醒办陷,最后在喚醒時繼續(xù)處理事件再次進入循環(huán)貌夕。 Runloop的內(nèi)部結(jié)構(gòu)與運行原理

5. Runloop的一些運用?
  • 使用NSTimer民镜、CADisplayLink時需要addTimer:forMode:啡专, 這個時候如果不希望滾動視圖時停止timer就需要傳入NSRunLoopCommonModes.
  • 在子線程中使用- (void)performSelector: withObject:afterDelay:時方法不會得到執(zhí)行,原因就是子線程runloop未開啟而這個方法的實現(xiàn)是開啟timer將timer加到了runloop制圈。
  • 常駐子線程見上面的實現(xiàn)们童。
  • iOS系統(tǒng)對主線程Runloop的狀態(tài)進行了監(jiān)聽addObserver:,在runloop開啟時創(chuàng)建了一個autoreleasepool鲸鹦,在進入休眠時銷毀這個autoreleasepool然后創(chuàng)建一個新的autoreleasepool慧库,在runloop退出時銷毀autoreleasepool,這樣完成了自動的對對象內(nèi)存的管理馋嗜。

推薦閱讀:
博客:NSRunloop簡單細說(六)—— 幾種循環(huán)模式詳細解析
官方文檔:Runloop官方文檔
源碼:CF框架源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末齐板,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子葛菇,更是在濱河造成了極大的恐慌甘磨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眯停,死亡現(xiàn)場離奇詭異济舆,居然都是意外死亡,警方通過查閱死者的電腦和手機庵朝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門吗冤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來又厉,“玉大人,你說我怎么就攤上這事椎瘟「仓拢” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵肺蔚,是天一觀的道長煌妈。 經(jīng)常有香客問我,道長宣羊,這世上最難降的妖魔是什么璧诵? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮仇冯,結(jié)果婚禮上之宿,老公的妹妹穿的比我還像新娘。我一直安慰自己苛坚,他們只是感情好比被,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泼舱,像睡著了一般等缀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娇昙,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天尺迂,我揣著相機與錄音,去河邊找鬼冒掌。 笑死噪裕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的宋渔。 我是一名探鬼主播州疾,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辜限,長吁一口氣:“原來是場噩夢啊……” “哼皇拣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起薄嫡,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤氧急,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后毫深,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吩坝,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年哑蔫,在試婚紗的時候發(fā)現(xiàn)自己被綠了钉寝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弧呐。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嵌纲,靈堂內(nèi)的尸體忽然破棺而出俘枫,到底是詐尸還是另有隱情,我是刑警寧澤逮走,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布鸠蚪,位于F島的核電站,受9級特大地震影響师溅,放射性物質(zhì)發(fā)生泄漏茅信。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一墓臭、第九天 我趴在偏房一處隱蔽的房頂上張望蘸鲸。 院中可真熱鬧,春花似錦窿锉、人聲如沸棚贾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妙痹。三九已至,卻和暖如春鼻疮,著一層夾襖步出監(jiān)牢的瞬間怯伊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工判沟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耿芹,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓挪哄,卻偏偏與公主長得像吧秕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子迹炼,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355