首先祷杈,iOS 開(kāi)發(fā)中能遇到兩個(gè)線程對(duì)象: pthread_t 和 NSThread已旧。過(guò)去蘋(píng)果有份文檔標(biāo)明了 NSThread 只是 pthread_t 的封裝冠骄,但那份文檔已經(jīng)失效了裂允,現(xiàn)在它們也有可能都是直接包裝自最底層的 mach thread廓旬。蘋(píng)果并沒(méi)有提供這兩個(gè)對(duì)象相互轉(zhuǎn)換的接口际插,但不管怎么樣绝葡,可以肯定的是 pthread_t 和 NSThread 是一一對(duì)應(yīng)的。比如腹鹉,你可以通過(guò) pthread_main_thread_np() 或 [NSThread mainThread] 來(lái)獲取主線程藏畅;也可以通過(guò) pthread_self() 或 [NSThread currentThread] 來(lái)獲取當(dāng)前線程。CFRunLoop 是基于 pthread 來(lái)管理的功咒。
蘋(píng)果不允許直接創(chuàng)建 RunLoop愉阎,它只提供了兩個(gè)自動(dòng)獲取的函數(shù):CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 這兩個(gè)函數(shù)內(nèi)部的邏輯大概是下面這樣:
/// 全局的Dictionary力奋,key 是 pthread_t榜旦, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 訪問(wèn) loopsDic 時(shí)的鎖
static CFSpinLock_t loopsLock;
/// 獲取一個(gè) pthread 對(duì)應(yīng)的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// 第一次進(jìn)入時(shí)景殷,初始化全局Dic溅呢,并先為主線程創(chuàng)建一個(gè) RunLoop澡屡。
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
/// 直接從 Dictionary 里獲取。
CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
if (!loop) {
/// 取不到時(shí)咐旧,創(chuàng)建一個(gè)
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
/// 注冊(cè)一個(gè)回調(diào)驶鹉,當(dāng)線程銷毀時(shí),順便也銷毀其對(duì)應(yīng)的 RunLoop铣墨。
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
return _CFRunLoopGet(pthread_self());
}
/// 全局的Dictionary室埋,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 訪問(wèn) loopsDic 時(shí)的鎖
static CFSpinLock_t loopsLock;
/// 獲取一個(gè) pthread 對(duì)應(yīng)的 RunLoop伊约。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// 第一次進(jìn)入時(shí)姚淆,初始化全局Dic,并先為主線程創(chuàng)建一個(gè) RunLoop屡律。
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
/// 直接從 Dictionary 里獲取腌逢。
CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
if (!loop) {
/// 取不到時(shí),創(chuàng)建一個(gè)
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
/// 注冊(cè)一個(gè)回調(diào)超埋,當(dāng)線程銷毀時(shí)上忍,順便也銷毀其對(duì)應(yīng)的 RunLoop。
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
return _CFRunLoopGet(pthread_self());
}
從上面的代碼可以看出纳本,線程和 RunLoop 之間是一一對(duì)應(yīng)的,其關(guān)系是保存在一個(gè)全局的 Dictionary 里腋颠。線程剛創(chuàng)建時(shí)并沒(méi)有 RunLoop繁成,如果你不主動(dòng)獲取,那它一直都不會(huì)有淑玫。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí)巾腕,RunLoop 的銷毀是發(fā)生在線程結(jié)束時(shí)。你只能在一個(gè)線程的內(nèi)部獲取其 RunLoop(主線程除外)絮蒿。
關(guān)于GCD
實(shí)際上 RunLoop 底層也會(huì)用到 GCD 的東西,但同時(shí) GCD 提供的某些接口也用到了 RunLoop尊搬, 例如 dispatch_async()。
當(dāng)調(diào)用 dispatch_async(dispatch_get_main_queue(), block) 時(shí)土涝,libDispatch 會(huì)向主線程的 RunLoop 發(fā)送消息佛寿,RunLoop會(huì)被喚醒,并從消息中取得這個(gè) block但壮,并在回調(diào) CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE() 里執(zhí)行這個(gè) block冀泻。但這個(gè)邏輯僅限于 dispatch 到主線程,dispatch 到其他線程仍然是由 libDispatch 處理的蜡饵。
代碼示例:
#import "ViewController.h"
#import "PJThread.h"
@interface ViewController ()
//創(chuàng)建一個(gè)NSRunLoop屬性,用于在線程執(zhí)行完后能在訪問(wèn)他的內(nèi)容
@property (nonatomic, strong)NSRunLoop *threadRunLoop;
//GCD方式
@property (nonatomic, strong)NSRunLoop *threadRunLoop2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
//PJThread為自定義的繼承NSThread的線程,被銷毀時(shí)會(huì)打印"線程銷毀了!"
PJThread *pjThread = [[PJThread alloc] initWithBlock:^{
//線程剛創(chuàng)建時(shí)并沒(méi)有 RunLoop弹渔,如果你不主動(dòng)獲取,那它一直都不會(huì)有溯祸。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí),這邊會(huì)創(chuàng)建一個(gè)與pjThread對(duì)應(yīng)的線程.你只能在一個(gè)線程的內(nèi)部獲取其 RunLoop(主線程除外)
weakSelf.threadRunLoop = [NSRunLoop currentRunLoop];
//為了能讓線程被銷毀weakSelf.threadRunLoop就不循環(huán)執(zhí)行(即不調(diào)用run)
NSLog(@"在線程block內(nèi)打印:%@",weakSelf.threadRunLoop);
}];
[pjThread start];
//到這邊時(shí)線程已經(jīng)執(zhí)行結(jié)束,被銷毀,他所對(duì)應(yīng)的runLoop也會(huì)被銷毀
NSLog(@"在線程執(zhí)行后打印:%@",self.threadRunLoop);
//和PJThread是一樣的效果
dispatch_async(dispatch_get_global_queue(0, 0), ^{
weakSelf.threadRunLoop2 = [NSRunLoop currentRunLoop];
//為了能讓線程被銷毀weakSelf.threadRunLoop2就不循環(huán)執(zhí)行(即不調(diào)用run)
NSLog(@"在dispatch_async block內(nèi)打印:%@",weakSelf.threadRunLoop2);
});
NSLog(@"在線程dispatch_async執(zhí)行后打印:%@",self.threadRunLoop2);
}
@end
PJThread代碼:
#import "PJThread.h"
@implementation PJThread
- (void)dealloc{
NSLog(@"線程銷毀了!");
}
@end
最終的打印效果:
2017-06-10 14:35:37.966179 RunLoopTest[11826:3392880] 在線程執(zhí)行后打印:(null)
2017-06-10 14:35:37.966392 RunLoopTest[11826:3392880] 在線程dispatch_async執(zhí)行后打印:(null)
2017-06-10 14:35:37.967431 RunLoopTest[11826:3392903] 在dispatch_async block內(nèi)打印:<CFRunLoop 0x17017d040 [0x1aa9abbb8]>{wakeup port = 0x5f03, stopped = false, ignoreWakeUps = true,
current mode = (none),
common modes = <CFBasicHash 0x1700520f0 [0x1aa9abbb8]>{type = mutable set, count = 1,
entries =>
2 : <CFString 0x1a486d470 [0x1aa9abbb8]>{contents = "kCFRunLoopDefaultMode"}
}
,
common mode items = (null),
modes = <CFBasicHash 0x170052510 [0x1aa9abbb8]>{type = mutable set, count = 1,
entries =>
2 : <CFRunLoopMode 0x170187df0 [0x1aa9abbb8]>{name = kCFRunLoopDefaultMode, port set = 0x6003, queue = 0x17017d100, source = 0x1701c6bd0 (not fired), timer port = 0x6203,
sources0 = (null),
sources1 = (null),
observers = (null),
timers = (null),
currently 518769338 (12655688486437) / soft deadline in: 7.68613809e+11 sec (@ -1) / hard deadline in: 7.68613809e+11 sec (@ -1)
},
}
}
2017-06-10 14:35:37.967823 RunLoopTest[11826:3392920] 在線程block內(nèi)打印:<CFRunLoop 0x17417c740 [0x1aa9abbb8]>{wakeup port = 0x5c03, stopped = false, ignoreWakeUps = true,
current mode = (none),
common modes = <CFBasicHash 0x1740533e0 [0x1aa9abbb8]>{type = mutable set, count = 1,
entries =>
2 : <CFString 0x1a486d470 [0x1aa9abbb8]>{contents = "kCFRunLoopDefaultMode"}
}
,
common mode items = (null),
modes = <CFBasicHash 0x1740532f0 [0x1aa9abbb8]>{type = mutable set, count = 1,
entries =>
2 : <CFRunLoopMode 0x174187c50 [0x1aa9abbb8]>{name = kCFRunLoopDefaultMode, port set = 0x5d03, queue = 0x17417c800, source = 0x1741c6630 (not fired), timer port = 0x6303,
sources0 = (null),
sources1 = (null),
observers = (null),
timers = (null),
currently 518769338 (12655688502251) / soft deadline in: 7.68613809e+11 sec (@ -1) / hard deadline in: 7.68613809e+11 sec (@ -1)
},
}
}
2017-06-10 14:35:37.970469 RunLoopTest[11826:3392920] 線程銷毀了!