從新認識 NSRunLoopCommonModes

最近寫一些關於線程保活/取消的測試 具體代碼以下bash

-(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]];
    }
}
複製代碼

關於 Runloop 的介紹我也不在此贅述。就說一下我遇到的問題. 在oop

[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
複製代碼

這句中,我嘗試着使用了 NSRunLoopCommonModes,來設定 Mode。就行 ruanloop 運行。 可是我會收到 Crash.測試

可是若是我使用 NSDefaultRunLoopMode 來填充這個 mode 就不會 crash.ui

那麼這是爲啥?spa

個人環境 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 的流程.3d

  • 跟着源碼先到了 Foundation 中.
  • Foundation 調用了 CoreFoundation 中的 CFRunLoopAddTimer(_rl, (CFRunLoopTimerRef)timer, (CFStringRef)mode);
  • 最後來到 CoreFoundation 中

  • Time 是對 mode 就好了特殊處理,而後添加到 rl->_commonModeItems 中,最終添加到新的 Commond 中
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
複製代碼

因此爲何 time 使用 Common 卻能夠,由於是由於對 Common 對了轉換,作了特殊處理。code

那麼,在來結合源碼看看orm

- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate
複製代碼

方法。cdn

他最終會走到 Foundation 中的 moreWork = CFRunLoopRunInMode((CFStringRef)mode, t, true) == kCFRunLoopRunHandledSource;方法中。

當返回值是 kCFRunLoopRunHandledSource 時,返回一個 true,來完成這次運行。

隨便在說一下這兩個方法,他們爲何不會退出。

  1. -(void)run;
  2. -(void)runUntilDate:(NSDate *)limitDate;

緣由是:他們會一直循環,因此沒法退出的.

接上,那麼底層的 CoreFoundation 會對於傳入不一樣的 mode 作特殊處理嗎? 帶着問題我翻看源碼。

SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
複製代碼

其實內部並無作什麼特殊處理,就是一系列判斷。

那麼爲何設置爲 Common 會 crash 了?

我有找了不少資料。

在一片文章中找到了以下內容:

  • 讓 Run Loop 運行在 NSRunLoopCommonModes下是沒有意義的,由於一個時刻 Run Loop 只能運行在一個特定模式下,而不多是個模式集合
  • 注意Run Loop不能在運行在NSRunLoopCommonModes模式,由於NSRunLoopCommonModes實際上是個模式集合,而不是一個具體的模式

因此會掛的緣由是由於: Common是一個模式合集,而非一個具體的模式,在這裏須要的是一個具體的模式,因此就會 crash.

另外:

CFRunLoopRun(); // 啓動
CFRunLoopStop(CFRunLoopGetCurrent()); // 中止
複製代碼

這兩個是能夠啓動和中止的.從源碼來看

  • 從源碼看,他的判斷條件是遇到了 Stopped 和 Finished 就會中止

中止方法,最終會設置中止值。

相關文章
相關標籤/搜索