这只是一个实验性的代码,但由于代码没有按我预期的那样执行,我感到困惑。
代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.myQueue = dispatch_queue_create("com.maxwell.timer", NULL);
dispatch_async(self.myQueue, ^{
self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"Hey!");
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
现在,我得到一个输出“嘿!”每1秒,这里没问题。我确实知道在调度线程中必须显式运行runloop。
我尝试停止计时器时出现问题。
- (void)stopTimer {
dispatch_async(self.myQueue, ^{
[self.timer invalidate];
self.Timer = nil;
});
}
实际上,块中的代码甚至无法执行!
而且,如果我在此处使用并发队列(
dispatch_asyn(dispatch_get_global_queue(...), ^{...})
)可以了。我知道的事情:每当我
dispatch_async
时,无论并发队列还是串行队列,代码都在不同的线程中执行。因此严格来说,我没有在添加了的同一个线程中使计时器无效,但在并发线程中却使了该计时器无效。所以我的问题是为什么它无法使串行队列无效?
最佳答案
问题是您有一个串行队列,您在该队列上调用 [[NSRunLoop currentRunLoop] run]
。但是您不会从该调用中返回(只要该运行循环中有计时器等)。正如 run
documentation所说:
如果没有输入源或计时器附加到运行循环,则此方法立即退出;否则,此方法将退出。否则,它通过重复调用NSDefaultRunLoopMode
在runMode:beforeDate:
中运行接收器。换句话说,此方法有效地开始了一个无限循环,该循环处理来自运行循环的输入源和计时器的数据。
这样会阻塞串行队列的线程。只要该线程被阻止,调度到该队列的任何代码(例如您试图使计时器无效)都不会运行。您有一个“Catch 22”。
最重要的是,如果您要设置一个后台线程来运行NSTimer
,则需要为此创建自己的线程,而不要使用GCD工作线程之一。有关示例,请参见https://stackoverflow.com/a/38031658/1271826。但是正如该答案所继续描述的那样,在后台线程上运行计时器的首选方法是分派计时器,这使您摆脱了操纵线程和运行循环的麻烦。
关于ios - 为什么我不能在dispatch_async串行队列中停止计时器?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52315181/