最近寫一些關(guān)于線程保活/取消的測試
具體代碼如下
-(void)test {
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 (!port) {
port = [NSPort port];
}
[runLoop addPort:port forMode:NSDefaultRunLoopMode];
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
關(guān)于 Runloop 的介紹我也不在此贅述。就說一下我遇到的問題.
在
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
這句中,我嘗試著使用了 NSRunLoopCommonModes,來設(shè)定 Mode。就行 ruanloop 運行。
但是我會收到 Crash.
但是如果我使用 NSDefaultRunLoopMode 來填充這個 mode 就不會 crash.
那么這是為啥?
我的環(huán)境
Xcode:10.2.1
Mac:macOS Mojave version 10.14.4
iPhone: iOS 12.2 iPhone 8 Plus 模擬器
我的思考
1. 使用 Timer 時,也可以使用 Common,但是依然可以使用。這里為啥不行?
2. Runloop 只能在一個特定的模式下運行,那么 Common 是不是因為包含了多種,所以不能正常使用?(但是 Common 包含了 Default)
帶著思考我去翻了源碼.先看看 Timer 的流程.
- 跟著源碼先到了 Foundation 中.
- Foundation 調(diào)用了 CoreFoundation 中的
CFRunLoopAddTimer(_rl, (CFRunLoopTimerRef)timer, (CFStringRef)mode); - 最后來到 CoreFoundation 中
- Time 是對 mode 就行了特殊處理,然后添加到
rl->_commonModeItems中,最終添加到新的 Commond 中
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
所以為什么 time 使用 Common 卻可以,因為是因為對 Common 對了轉(zhuǎn)換,做了特殊處理。
那么,在來結(jié)合源碼看看
- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate
方法。
他最終會走到 Foundation 中的 moreWork = CFRunLoopRunInMode((CFStringRef)mode, t, true) == kCFRunLoopRunHandledSource;方法中。
當(dāng)返回值是 kCFRunLoopRunHandledSource 時,返回一個 true,來完成此次運行。
隨便在說一下這兩個方法,他們?yōu)槭裁床粫顺觥?/p>
- -(void)run;
- -(void)runUntilDate:(NSDate *)limitDate;
原因是:他們會一直循環(huán),所以無法退出的.
接上,那么底層的 CoreFoundation 會對于傳入不同的 mode 做特殊處理嗎?
帶著問題我翻看源碼。
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
其實內(nèi)部并沒有做什么特殊處理,就是一系列判斷。
那么為什么設(shè)置為 Common 會 crash 了?
我有找了很多資料。
在一片文章中找到了如下內(nèi)容:
- 讓 Run Loop 運行在 NSRunLoopCommonModes下是沒有意義的,因為一個時刻 Run Loop 只能運行在一個特定模式下,而不可能是個模式集合
- 注意Run Loop不能在運行在NSRunLoopCommonModes模式,因為NSRunLoopCommonModes其實是個模式集合,而不是一個具體的模式
所以會掛的原因是因為: Common是一個模式合集,而非一個具體的模式,在這里需要的是一個具體的模式,所以就會 crash.
另外:
CFRunLoopRun(); // 啟動
CFRunLoopStop(CFRunLoopGetCurrent()); // 停止
這兩個是可以啟動和停止的.從源碼來看
- 從源碼看,他的判斷條件是遇到了 Stopped 和 Finished 就會停止
停止方法,最終會設(shè)置停止值。