【发布时间】:2011-08-26 13:42:21
【问题描述】:
在我的应用程序中,我让进度指示器在发送 HTTP 请求之前启动动画。 完成处理程序在块中定义。获得响应数据后,我将进度指示器隐藏在块内。我的问题是,据我所知,UI 更新必须在主线程中执行。如何确定?
如果我在窗口控制器中定义一个更新UI的方法,并让块调用该方法而不是直接更新UI,这是一个解决方案吗?
【问题讨论】:
标签: cocoa-touch cocoa objective-c-blocks
在我的应用程序中,我让进度指示器在发送 HTTP 请求之前启动动画。 完成处理程序在块中定义。获得响应数据后,我将进度指示器隐藏在块内。我的问题是,据我所知,UI 更新必须在主线程中执行。如何确定?
如果我在窗口控制器中定义一个更新UI的方法,并让块调用该方法而不是直接更新UI,这是一个解决方案吗?
【问题讨论】:
标签: cocoa-touch cocoa objective-c-blocks
此外,如果您的应用面向 iOS >= 4,您可以使用 Grand Central Dispatch:
dispatch_async(dispatch_get_main_queue(), ^{
// This block will be executed asynchronously on the main thread.
});
当您的自定义逻辑无法用 performSelect… 方法采用的单个选择器和对象参数轻松表达时,这很有用。
要同步执行块,请使用dispatch_sync() - 但请确保您当前不在主队列上执行,否则 GCD 将死锁。
__block NSInteger alertResult; // The __block modifier makes alertResult writable
// from a referencing block.
void (^ getResponse)() = ^{
NSAlert *alert = …;
alertResult = [NSAlert runModal];
};
if ([NSThread isMainThread]) {
// We're currently executing on the main thread.
// We can execute the block directly.
getResponse();
} else {
dispatch_sync(dispatch_get_main_queue(), getResponse);
}
// Check the user response.
if (alertResult == …) {
…
}
【讨论】:
dispatch_sync() 到您的代码当前正在执行的队列中(GCD 会死锁)。但这不适用于使用dispatch_async() 的 asynchronous 方法,如上面的答案示例中所示。如果您需要dispatch_sync(dispatch_get_main_queue(), …),请确保dispatch_get_current_queue() 不是主队列。 [NSThread isMainThread] 将在您的示例中起到相同的作用。
你可能误会了什么。使用块并不意味着您的代码在后台线程中运行。有许多插件异步工作(在另一个线程中)并使用块。
有几个选项可以解决您的问题。
您可以使用[NSThread isMainThread] 检查您的代码是否在主线程中运行。这可以帮助您确保您不在后台。
您还可以使用performSelectorInMainThread:SEL 或performSelectorInBackground:SEL 在主或后台执行操作。
当您尝试从 bakcground 线程调用 UI 时,应用程序会立即崩溃,因此很容易找到错误。
【讨论】: