为什么必须在主线程上执行UIKit操作?

我想了解为什么UI操作不能使用multithreading执行。 这也是其他框架,如OpenGL或cocos2d的要求?

其他语言像C#和JavaScript怎么样? 我试图在谷歌,但人们提到一些关于POSIX线程,我不明白。

在Cocoa Touch中, UIApplication即应用程序的实例)被附加到主线程,因为此线程是由Cocoa Touch的入口点函数UIApplicatioMain()创build的。 它设置了主事件循环,包括应用程序的运行循环,并开始处理事件。 应用程序的主事件循环接收所有的UI事件,即触摸,手势等

从文档UIApplicationMain()

该函数实例化来自主类的应用程序对象,并实例化来自给定类的委托(如果有)并设置应用程序的委托。 它还设置了主事件循环,包括应用程序的运行循环,并开始处理事件。 如果应用程序的Info.plist文件指定要加载的主要nib文件,则通过包含NSMainNibFile键和该值的有效nib文件名称,该函数将加载该nib文件。

这些应用程序的用户界面事件进一步转发到UIResponder的下面的响应者通常像UIApplication – > UIWindow – > UIViewController – > UIView – >子视图( UIButton等)链。

响应者处理像button按下,点击,捏缩放,滑动等事件,这些在UI中被转换为变化。 因此,正如你所看到的,这些事件链发生在主线程上,这就是为什么UIKit ,包含响应者的框架应该在主线程上运行。

从docs再次UIKit

大多数情况下,UIKit类只能在应用程序的主线程中使用。 对于从UIResponder派生的类或者以任何方式涉及操纵应用程序用户界面的类尤其如此。

编辑

为什么drawRect需要在主线程上?

drawRect:UIKit作为UIView生命周期的一部分调用。 所以drawRect:被绑定到主线程。 以这种方式绘制是昂贵的,因为它使用主线程上的CPU来完成。 通过使用CALayer技术(核心animation)提供硬件加速graphics。

另一方面, CALayer作为视图的后盾存储。 该视图将只显示当前状态的caching位图。 对视图属性所做的任何更改都会导致在备份副本上由GPU执行的后备存储更改。 但是,该视图仍然需要提供初始内容并定期更新视图。 我没有真正的OpenGL的工作,但我认为它也使用图层(我可能是错的)。

尽我所知,我试图回答这个问题。 希望有所帮助!

C#的行为相同(请参阅例如: 保持UI线程响应 )。 UI更新必须在UI线程中完成 – 大部分其他事情应该在后台进行。

如果不是这样的话,那么在UI中必须要做的所有更新之间可能会有一个同步的地狱…

来自: https : //www.objc.io/issues/2-concurrency/thread-safe-class-design/

这是从苹果方面有意识的devise决定,没有UIKit是线程安全的。 使其线程安全不会在性能方面给你带来太多的收益; 实际上会让很多事情变慢。 UIKit绑定到主线程的事实使得编写并发程序和使用UIKit变得非常容易。 你所要做的就是确保对UIKit的调用总是在主线程上进行。

所以根据这个事实,必须在主线程上访问UIKit对象是由苹果公司devise的决定,以支持性能。

因为您希望用户能够在发生UI时看到UI更改。 如果您能够在后台线程中执行UI更改并在完成时显示它们,则看起来该应用程序的行为不正确。

所有非UI操作(或者至less那些代价非常昂贵的操作,比如下载东西或者进行数据库查询)应该在后台线程上进行,而所有的UI改变必须总是在主线程上进行,以便为用户提供stream畅的performance经验可能。

我不知道C#中的Windows Phone应用程序是什么样的,但我期望它是一样的。 在Android上,系统甚至不会让你在主线程上下载,直接创build后台线程。

作为一个经验法则 – 当你认为主线,想“用户看到”。

每个系统,每个图书馆都需要关注线程的安全性,并且必须做一些事情来保证线程的安全,同时要保证线程的正确性和性能。

在iOS和MacOS X用户界面的情况下,通过仅允许在主线程上调用和执行UI方法来做出UI线程安全的决定。 就是这样。

由于有很多复杂的事情需要至less序列化,以防止总的混乱发生,我看不到从后台线程上允许UI获得很多。