可行性
在 常驻线程是一种什么体验 这篇文章中给大家分享了如何利用 RunLoop 的特性, 结合 NSMachPort
实现一个 常驻线程
的主题内容.
今天我们探讨一下使用 NSTimer
如何实现 常驻线程
以及注意事项.
从 RunLoop 的特性来看, 只要有 Source 或者 Timer 都会使其能自循环使用, 不会立即终止当前线程的执行, 所以从理论上来看 NSTimer
是可以达到创建 常驻线程
的目的的.
开始实践
完整的例子代码, 可以从文章的附录获取和查看, 这里只给出核心代码.
创建线程
1 | - (NSThread *)permanentThread { |
线程执行的函数
1 | - (void)asyncRun { |
创建定时器
1 | - (void)_attachTimerToRunLoop { |
可以看出, _attachTimerToRunLoop
中是将 timer 加入到当前的 RunLoop 当中了. 这里注意, repeats 值被设置为 YES
了.
跟之前一样, 可以使用点击事件来模拟和验证常驻线程的有效性.
1 | - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { |
每点击一次屏幕, 都会对应执行 runAnyTime
里面的内容.
对 repeat 的思考
在上面的示例中, 我将 repeat
参数设置为 YES
, 试想一下如果把 repeat
参数设置为 NO
, 会不会造成常驻线程失效呢?
动手试试…
1 | - (void)_attachTimerToRunLoop { |
再次点击屏幕若干次, 同样会执行对应函数里面的内容. 这就说明了即使将 repeat
参数设置为 NO
, 也不会影响常驻线程.
那我们再来点具有挑战的活动…
将当前页面加入 UIScrollview
这个视图, 还是保持 repeat
参数设置为 NO
.
1 | - (void)viewDidLoad { |
因为加入了滚动视图, 我们换一种方式来模式和验证常驻线程.
在 UIScrollview
代理中来模拟, 示例如下:
1 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { |
运行后进入该页面, 可以发现常驻线程被终止了.
1 | veryitman--timerRun. |
当除我以为更换一下模式即使 将 repeat
参数设置为 NO
, 也不会出现常驻线程被终止的问题. 如下面的代码:
1 | _timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(runTimer) userInfo:nil repeats:NO]; |
这样更换模式为 NSRunLoopCommonModes
也不行.
在这种情况(有滚动视图的)下, 将 repeat
参数设置为 YES
就不会导致常驻线程被终止了, 无论哪种方式创建的 Timer.
总结
1.子线程创建中的 RunLoop 的模式不会与主线程中 RunLoop 的模式冲突, 各自运行在各自的 mode 当中.
2.使用 NSTimer
来创建常驻线程, 在有 UIScrollview
或者其子类的情况下, 需要将 repeats
设置为 YES
, 否则不会创建常驻线程. 没有滚动视图的情况下, repeats
设置为 NO
也没有关系.
3.创建 NSTimer
下面两种创建 Timer 的效果是一致的.
1 | [NSTimer scheduledTimerWithTimeInterval:2 |
scheduledTimerWithTimeInterval
默认会将 Timer 加入到当前的 RunLoop 中.
1 | [NSTimer timerWithTimeInterval:2 target:self selector:@selector(runTimer) userInfo:nil repeats:NO]; |
附录
完整示例代码
1 | #import "MZTimerPermanentThreadController.h" |