为什么从前台调用某些东西会崩溃,而从后台调用它却不会?

Why would calling something from the foreground crash, when calling it from the background does not?

好的,(又)对 ObjectiveC 比较陌生,来自 Swift 背景。在我们的代码库中,我们有这行代码...

return _queryURL && ![[UIApplication sharedApplication] canOpenURL:_queryURL];

虽然它可以工作,但在调试时,Xcode 会发出警告,指出 canOpenURL 必须从前台线程执行。

'Simple enough' 我想...'我会在前台线程上同步调用它!'然后我更新代码如下...

__block BOOL result = NO;
  
dispatch_sync(dispatch_get_main_queue(), ^{
  result = _queryURL && ![[UIApplication sharedApplication] canOpenURL:_queryURL];
});

return result;

...但是现在,当执行调用 canOpenURL 的行时,我得到这个...

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

咦?!为什么将其推送到主线程会导致 EXC_BAD_INSTRUCTION 崩溃?那是一条红鲱鱼吗?

更重要的是,您如何解决这个问题?

我已经看到 错误指令 错误发生在你 sync 在主线程上调用某些东西时 已经 在主线程上。

我认为,根据您收到的错误和警告,您有时会在后台调用它,有时会在 main 上调用它。因此,您需要使用例如

进行检查
NSThread.isMainThread

或类似的在你打电话之前,然后根据那个继续,也许

if ( NSThread.isMainThread )
{
  // my call
}
else
{
  dispatch_sync( dispatch_get_main_queue (), ^ {
    // my call
  } );
}

但是,如果确实是有时从主调用有时从后台调用的情况,你还需要验证其背后的逻辑是否合理。

为了补充正确答案,我 运行 经常参与其中(通常在 属性 观察者中),我为此创建了一个函数:

void ExecuteOnMain( dispatch_block_t block )
{
    // Shorthand for synchronously executing a block on the main thread before returning.
    // Unlike dispatch_sync(), this won't deadlock if executed on the main thread.
    if (NSThread.isMainThread)
        block(); // main thread: execute the block immediately
    else
        dispatch_sync(dispatch_get_main_queue(),block); // not the main thread, queue the block
}

那么,你只需要写

__block BOOL result = NO;
  
ExecuteOnMain(), ^{
  result = _queryURL && ![[UIApplication sharedApplication] canOpenURL:_queryURL];
});

return result;