当 Cocoa 应用程序中的主线程被阻塞时,UI 不会更新

Posted

技术标签:

【中文标题】当 Cocoa 应用程序中的主线程被阻塞时,UI 不会更新【英文标题】:UI does not update when main thread is blocked in Cocoa app 【发布时间】:2012-04-05 18:35:22 【问题描述】:

我在我的主线程中使用 NSProgressIndicator 来更新我运行整个方法时的进度。现在,当我最终从不同的类文件中调用一个对象,并等待该对象返回到我的主线程的值时,我注意到 NSProgressIndicator 将消失。我知道这是因为在我从另一个对象获得返回值之前,主线程被阻塞了。

所以我的问题是在主线程中更新 UI 而不阻塞它并让其他对象在后台运行并根据需要向主线程返回值的推荐方法是什么。我知道如何使用块,但不允许块操作返回值。 我需要的是帮助这个伪代码的东西:

-(IBAction) main 

//Update progress indicator UI to show progress
//perform an call to another object from another class.
// wait till i get its return value.
//Update progress indicator UI to show progress
// Use this return value to do something.
//Update progress indicator UI to show progress



当调用另一个对象时,我注意到我已经完全消失了确定的 NSProgressIndicator,因为主线程被阻塞了。谢谢。

【问题讨论】:

【参考方案1】:

您上面的代码不是正确的方法。由于main 永远不会返回,因此进度指示器永远不会更新。您必须在主线程上快速返回。

相反,您要做的是设置一个后台块,在各个点更新主线程上的进度指示器。所以,例如:

- (IBAction)start:(id)sender 
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

  dispatch_async(queue, ^
    dispatch_async(dispatch_get_main_queue(), ^[self.progress setProgress:0];);

    // Doing some stuff
    dispatch_async(dispatch_get_main_queue(), ^[self.progress setProgress:.25];);

    // Doing more stuff
    dispatch_async(dispatch_get_main_queue(), ^[self.progress setProgress:.75];);
  );

(是的,这会导致队列保留self,但这没关系,因为self 没有保留队列。)

【讨论】:

【参考方案2】:

您可以使用 GCD (Grand Central Dispatch) 实现您想要的。

下面是一个让您入门的示例:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^
        // Perform async operation
                dispatch_sync(dispatch_get_main_queue(), ^
                    // Update UI
                );
    );

【讨论】:

不要在这里打电话给dispatch_release。你没有创建这个队列,所以你不应该释放它。 @RobNapier 你说得对。编辑了我的答案,感谢您的注意 当我添加此代码时,我收到错误消息,提示找不到我的其他方法定义。有什么想法吗?【参考方案3】:

听起来您的操作应该在单独的线程中运行,这可以通过多种方式完成,但使用 NSOperationQueue 和自定义 NSOperation 类(设置这些比听起来更容易)或使用 NSInvokeOperation 可能最容易实现类。

然后,您可以使用 NSNotificationCenter 在主线程中将消息发送回您的班级,或使用键值观察 (KVO) 设置为观察者。

归根结底,您有多种选择,要做出最好的选择,您应该了解底层技术。我会亲自从 Apple 的 Threaded Programming Guide 开始,然后再读一遍,以确保您在构建解决方案之前提取了所有优点。

【讨论】:

以上是关于当 Cocoa 应用程序中的主线程被阻塞时,UI 不会更新的主要内容,如果未能解决你的问题,请参考以下文章

加载 DLL 阻塞 UI 线程

AsyncTask的使用

转载AsyncTask用法

如何追踪阻塞主(UI)线程的调用?

带有 DiffUtil (ListAdapter) 的 RecyclerView 会阻塞 UI 线程

在主线程上处理大型全局对象时如何不阻止来自工作线程的主 UI 线程