在后台线程上创建一个视图,将其添加到主线程上的主视图

Posted

技术标签:

【中文标题】在后台线程上创建一个视图,将其添加到主线程上的主视图【英文标题】:creating a view on a background thread, adding it the main view on the main thread 【发布时间】:2013-02-24 06:18:44 【问题描述】:

我是 Objective C 的新手,来自 .NET 和 java 背景。

所以我需要异步创建一些 UIwebviews,我在自己的队列中使用

     dispatch_queue_t queue = dispatch_queue_create("myqueue", NULL);
     dispatch_async(queue, ^
        // create UIwebview, other things too
             [self.view addSubview:webView];
        );

正如你想象的那样,这会引发错误:

   bool _WebTryThreadLock(bool), 0xa1b8d70: Tried to obtain the web lock from a thread other  
   than the main thread or the web thread. This may be a result of calling to UIKit from a  
   secondary thread. Crashing now...

那么如何在主线程上添加子视图呢?

【问题讨论】:

【参考方案1】:

因为您已经在使用调度队列。我不会使用performSelectorOnMainThread:withObject:waitUntilDone:,而是在主队列上执行子视图添加。

dispatch_queue_t queue = dispatch_queue_create("myqueue", NULL);
dispatch_async(queue, ^
    // create UIwebview, other things too

    // Perform on main thread/queue
    dispatch_async(dispatch_get_main_queue(), ^
        [self.view addSubview:webView];
    );
);

可以在后台队列中实例化UIWebView。但是要将其添加为子视图,您必须在主线程/队列上。来自UIView 文档:

线程注意事项

对应用程序用户界面的操作必须在主线程上进行。因此,您应该始终从应用程序主线程中运行的代码调用 UIView 类的方法。唯一可能不是绝对必要的情况是创建视图对象本身但所有其他操作都应在主线程上进行。

【讨论】:

当心,即使 initWithFrame 也不能保证是线程安全的。见***.com/questions/11122957/… 也许我对“线程安全”的理解是错误的。但我认为线程安全意味着可以同时从多个线程操作一个对象。例如。你可以想到一个线程安全的数组,它可以让你从多个线程添加对象。但是,在这种情况下,我们不会同时从多个线程/队列操作或访问视图对象。我们在一个线程/队列上创建它,然后基本上将其移交给另一个。 原来我链接的问题的答案具有误导性 - 抱歉。苹果说The only time this may not be strictly necessary is when creating the view object itself所以它应该是安全的。 我的应用在后台模式下被操作系统唤醒。我需要根据某个用户位置向keyWndow 添加一个子视图。这对我的用例来说足够了吗? ***.com/questions/46584566/…【参考方案2】:

大多数 UIKit 对象,包括UIView 的实例,必须从主线程/队列中操作。您不能在任何其他线程或队列上向UIView 发送消息。这也意味着您不能在任何其他线程或队列上创建它们。

【讨论】:

这并不完全正确。在后台实例化视图是完全可以的。来自 UIView 文档:对应用程序用户界面的操作必须发生在主线程上。因此,您应该始终从应用程序主线程中运行的代码调用 UIView 类的方法。唯一可能不是绝对必要的情况是创建视图对象本身但所有其他操作都应在主线程上进行。 @Florian 如果两个视图都不与 UI 交互,在后台使用方法 addSubview 是否正确? @BohdanSavych 我不确定。你的用例是什么?【参考方案3】:

正如 rob 所说,UI 更改应该只在主线程上完成。您正在尝试从辅助线程添加。将您的代码 [self.view addSubview:webView]; 更改为

[self.view performSelectorOnMainThread:@selector(addSubview:) withObject:webView waitUntilDone:YES];

【讨论】:

以上是关于在后台线程上创建一个视图,将其添加到主线程上的主视图的主要内容,如果未能解决你的问题,请参考以下文章

UILabel 没有使用线程立即显示

无法从 iOS6 中的后台线程调用主线程上的代码

将 NSManagedObject(在主上下文中创建)从后台线程传递到主线程是不是安全?

为啥即使在指定返回 - 后台线程到主线程问题后代码也会执行?

从后台线程添加到 UITableView 中的 UIStackView

在后台创建的 NSManagedObject 变成了主线程上的错误