重新认识 NSRunLoopCommonModes

最近写一些关于线程保活/取消的测试 具体代码如下

-(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 的介绍我也不在此赘述。就说一下我遇到的问题. 在

[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
复制代码

这句中,我尝试着使用了 NSRunLoopCommonModes,来设定 Mode。就行 ruanloop 运行。 但是我会收到 Crash.

但是如果我使用 NSDefaultRunLoopMode 来填充这个 mode 就不会 crash.

那么这是为啥?

我的环境 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 调用了 CoreFoundation 中的 CFRunLoopAddTimer(_rl, (CFRunLoopTimerRef)timer, (CFStringRef)mode);
  • 最后来到 CoreFoundation 中

  • Time 是对 mode 就行了特殊处理,然后添加到 rl->_commonModeItems 中,最终添加到新的 Commond 中
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
复制代码

所以为什么 time 使用 Common 却可以,因为是因为对 Common 对了转换,做了特殊处理。

那么,在来结合源码看看

- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate
复制代码

方法。

他最终会走到 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 就会停止

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值