我正在使用 Objective-C 开发 iOS 框架。
我dispatch_queue_t
通过使用创建了一个dispatch_queue_create
. 并调用CFRunLoopRun()
运行队列中的runloop。
但是,看起来dispatch_queue_t共享了RunLoop。有些类添加了一个无效的计时器,当我调用它时CFRunLoopRun()
,它在我这边崩溃了。
- (void)viewDidLoad {
[super viewDidLoad];
self.queue1 = dispatch_queue_create("com.queue1", DISPATCH_QUEUE_CONCURRENT);
self.queue2 = dispatch_queue_create("org.queue2", DISPATCH_QUEUE_CONCURRENT);
}
- (IBAction)btnButtonAction:(id)sender {
dispatch_async(self.queue1, ^{
NSString *runloop = [NSString stringWithFormat:@"%@", CFRunLoopGetCurrent()];
runloop = [runloop substringWithRange:NSMakeRange(0, 22)];
NSLog(@"Queue1 %p run: %@", self.queue1, runloop);
//NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:1 target:self selector:@selector(wrongSeletor:) userInfo:nil repeats:NO];
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
});
dispatch_async(self.queue2, ^{
NSString *runloop = [NSString stringWithFormat:@"%@", CFRunLoopGetCurrent()];
runloop = [runloop substringWithRange:NSMakeRange(0, 22)];
NSLog(@"Queue2 %p run: %@", self.queue2, runloop);
CFRunLoopRun();
});
}
有时他们采用相同的 RunLoop:
https://isstatic.askoverflow.dev/wGcv3.png
=====
您可以通过取消注释代码来查看崩溃情况NSTimer
。NSTimer已添加,但调用queue1
时仍在运行。CFRunLoopRun()
queue2
我读过一些描述,例如:需要一些有关调度队列、线程和 NSRunLoop 的说明
他们告诉我们:system creates a run loop for the thread
。但是,在我的检查中,他们正在共享 RunLoop。
这对我来说很伤心,因为我在调用生产时面临着崩溃的情况CFRunLoopRun()
。
太长了;博士
该运行循环代码存在几个问题。但是,如果目的只是在调度队列上运行计时器,我们通常会使用调度源计时器,从而消除大部分混乱。
典型的运行循环模式需要创建一个专用线程,获取该线程的运行循环,并调用“run”方法之一在该线程上旋转,处理运行循环事件,例如计时器事件。
调度队列是一种不同的方法。当我们将“工作项”分派到分派队列时,该队列将从线程池中获取(随机)工作线程,在该工作线程上运行该工作项,并且当工作项完成时,返回该工作线程线程到线程池,使其可用于将来的调度。
因此,这导致我们对您的代码片段进行了一些观察:
派遣到
queue1
没有任何意义。您打印一个运行循环,但不对其进行“运行”,而是直接完成,从而将工作线程返回到线程池。该线程现在可供未来的 GCD 调度再次使用。只有当您使用“run”方法旋转该运行循环,从而将该线程绑定到该运行循环并防止 GCD 重用该线程时,这才有意义。在这种情况下使用并发队列会令人困惑。运行循环在给定线程上旋转。为此目的创建并发队列是没有意义的。当运行循环在工作线程之一上旋转时,该队列上将同时完成哪些工作?它表明了对运行循环如何工作的概念性误解。
在 GCD 工作线程上无限期地旋转运行循环的想法根本是不明智的。调度队列的一般概念是,它们通过从池中获取现有工作线程、执行工作项并将该线程返回到池中来实现效率。无限期地占用工作线程的想法可以说与 GCD 背后的激励想法是对立的。你可以这样做,但一般来说,我们会避免无限期地占用 GCD 工作线程。
如果您在学术上对运行循环的工作原理感兴趣,那么这种探索就很好。但是,如果您只想在调度队列上运行计时器,那么运行循环
NSTimer
是错误的方法。调度计时器会简单得多(见下文)。如果你想在调度队列上运行某些东西,我们通常不使用
NSTimer
. 相反,我们将使用调度计时器。这根本不需要运行循环。与 a 不同,
NSTimer
我们需要保留对计时器的强引用:然后,启动计时器:
要停止计时器,请释放它: