【发布时间】:2015-04-28 07:30:36
【问题描述】:
我正在尝试调试一些包含以下错误消息的 iOS 崩溃日志:
*** 由于未捕获的异常 'NSDestinationInvalidException' 导致应用程序终止,原因:'*** -[SomeClass performSelector:onThread:withObject:waitUntilDone:modes:]: 目标 等待执行时线程退出
代码的相关部分是:
- (void) runInvocationOnMyThread:(NSInvocation*)invocation {
NSThread* currentThread = [NSThread currentThread];
if (currentThread != myThread) {
//call over to the correct thread
[self performSelector:@selector(runInvocationOnMyThread:) onThread:myThread withObject:invocation waitUntilDone:YES];
}
else {
//we're okay to invoke the target now
[invocation invoke];
}
}
这与问题discussed here 类似,只是我不想取消我的onThread: 线程。事实上,在我的情况下,onThread: 正在传递对应用程序主线程的引用,因此除非整个应用程序终止,否则它应该不可能终止。
所以第一个问题是,错误消息中提到的“目标”线程是我传递给onThread: 的线程,还是在onThread: 线程上等待调用完成的线程?
我假设这是第二种选择,好像主线程真的已经终止了后台线程的崩溃无论如何都没有实际意义。
考虑到这一点,并基于reference docs 对performSelector:onThread:... 的以下讨论:
特别注意事项
这个方法注册到它的runloop 当前上下文,并且取决于该 runloop 在常规上运行 正确执行的基础。您可能会调用的一种常见上下文 这个方法并最终注册了一个不是 定期自动运行是在被 调度队列。如果您在运行时需要这种类型的功能 一个调度队列,你应该使用 dispatch_after 和相关的方法来 得到你想要的行为。
...我已修改我的代码,使其更喜欢使用 GCD 而不是 performSelector:onThread:...,如下所示:
- (void) runInvocationOnMyThread:(NSInvocation*)invocation {
NSThread* currentThread = [NSThread currentThread];
if (currentThread != myThread) {
//call over to the correct thread
if ([myThread isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
[invocation invoke];
});
}
else {
[self performSelector:@selector(runInvocationOnMyThread:) onThread:myThread withObject:invocation waitUntilDone:YES];
}
}
else {
//we're okay to invoke the target now
[invocation invoke];
}
}
这似乎工作正常(虽然不知道它是否修复了崩溃,因为它是极其罕见的崩溃)。也许有人可以评论一下这种方法是否比原来更容易崩溃?
无论如何,主要问题是当目标线程是主线程时,只有一种明显的方式可以使用 GCD。就我而言,这是真的,但我希望能够使用 GCD,无论目标线程是否是主线程。
所以更重要的问题是,有没有办法从任意的NSThread 映射到 GCD 中的相应队列?理想情况下类似于dispatch_queue_t dispatch_get_queue_for_thread(NSThread* thread),这样我就可以将我的代码修改为:
- (void) runInvocationOnMyThread:(NSInvocation*)invocation {
NSThread* currentThread = [NSThread currentThread];
if (currentThread != myThread) {
//call over to the correct thread
dispatch_sync(dispatch_get_queue_for_thread(myThread), ^{
[invocation invoke];
});
}
else {
//we're okay to invoke the target now
[invocation invoke];
}
}
这可能吗,还是没有可以应用的从NSThread 到 GCD 队列的直接映射?
【问题讨论】:
-
您可能想要解释为什么(如果有原因的话)您完全关心线程关联性(对于除主线程以外的任何线程。)如果您关心的唯一线程关联性是主线程线程,那么在绝大多数情况下,
+[NSThread isMainThread]和dispatch_async(dispatch_get_main_queue(), ...)应该就是你所需要的。 -
@ipmcc - 代码本质上是为非线程安全的第三方库提供了一个线程安全的包装器,它要求对它的所有调用都在用于初始化它。虽然这是常见的做法,但至少在我正在使用的代码中,要在主线程上初始化库,这不是严格的要求,因此我对可以与任何线程一起使用的解决方案感兴趣。涵盖那些“假设”场景。
标签: ios objective-c multithreading grand-central-dispatch