从新认识 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 就会中止

中止方法,最终会设置中止值。

相关文章
相关标签/搜索