上一篇中問題, 實現(xiàn)"常駐線程"的方案
上一篇"RunLoop 學(xué)習(xí)筆記"中是這么介紹常駐線程, 以及對應(yīng)實現(xiàn)方法的:
即讓子線程處于 "不消亡" 的狀態(tài), 一直在后臺處理某些頻發(fā)事件 / 等待其他線程發(fā)來消息
- 在子線程監(jiān)控網(wǎng)絡(luò)狀態(tài)
- 在子線程開啟一個定時器
- 在子線程長期監(jiān)控其他行為
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run];
}
}
摘自 AFNetworking 源代碼, AFN這樣做的原理在于子線程下默認不開啟 RunLoop, 需要手動開啟, 而 RunLoop 不斷跑圈需要滿足以下條件之一 :
- RunLoop有事件源(輸入源), 包含基于端口 (port) 的事件源 / custom 事件源等
- RunLoop存在定時器
因此, AFN為RunLoop的default模式增加了一個NSMachPort端口(實際上也可以是其他端口),也就相當于為RunLoop添加了事件源, 因此RunLoop可以不斷的跑圈, 保證線程的不死狀態(tài)
順便提一下, AFN保持一個常駐線程的原因, 第一是因為子線程默認不會開啟RunLoop, 它會像一個C語言程序一樣運行完所有代碼后退出線程, 而網(wǎng)絡(luò)請求是異步的, 這就可能會出現(xiàn)通過網(wǎng)絡(luò)請求獲取到數(shù)據(jù)之后, 線程已經(jīng)退出, 無法執(zhí)行請求成功/失敗的代理方法, 因此AFN開啟了一個RunLoop, 惫迹活了線程
這樣做存在內(nèi)存泄漏的問題
經(jīng)過研究 bestswifter 的文章 深入研究 Runloop 與線程毙角埃活 的相關(guān)思想與結(jié)論, 對以前提出的方案做改進, 并鳴謝該作者
解決方案如下:
- (void)memoryTest {
for (int i = 0; i < 100000; ++i) {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
[self performSelector:@selector(stopThread) onThread:thread withObject:nil waitUntilDone:YES];
}
}
- (void)stopThread {
CFRunLoopStop(CFRunLoopGetCurrent());
NSThread *thread = [NSThread currentThread];
[thread cancel];
}
- (void)run {
@autoreleasepool {
NSLog(@"current thread = %@", [NSThread currentThread]);
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
if (!self.emptyPort) {
self.emptyPort = [NSMachPort port];
}
[runLoop addPort:self.emptyPort forMode:NSDefaultRunLoopMode];
[runLoop runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];
}
}
文/bestswifter(簡書作者)
原文鏈接:http://www.reibang.com/p/10121d699c32
著作權(quán)歸作者所有啊央,轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)麻惶,并標注“簡書作者”睦疫。
關(guān)于 RunLoop 線程碧ⅲ活技術(shù)更深層次的理解, 請移步至 bestswifter 的文章 深入研究 Runloop 與線程敝拗活