在 Apple 的 Cocoa API 中,为啥从主线程调用 NSApplicationMain 很重要?

Posted

技术标签:

【中文标题】在 Apple 的 Cocoa API 中,为啥从主线程调用 NSApplicationMain 很重要?【英文标题】:In Apple's Cocoa API, why is it important that NSApplicationMain be called from the main thread?在 Apple 的 Cocoa API 中,为什么从主线程调用 NSApplicationMain 很重要? 【发布时间】:2011-11-17 13:35:21 【问题描述】:

在NSApplicationMain 的文档中,它说:

创建应用程序,从应用程序的主包中加载主 nib 文件,然后运行应用程序。您必须从应用程序的主线程调用此函数 [...]。

“主线程”显然是指程序的第一个线程,main(argc, argv) 开始的地方。快速浏览NSThread 文档会发现+ (BOOL)isMainThread,它可用于确定当前线程是否是“主”线程。我进行了一些测试:无论是否调用了 NSApplicationMain,此方法都有效。

我的问题有两个(有些相关的)部分:

    NSApplicationMain 的主线程有什么特别之处? Cocoa 首先是如何识别主线程的?

【问题讨论】:

我认为无论你调用NSApplicationMain 的线程都会成为“主”线程,运行循环发生的地方。 这不是真的。我拼凑了一个简单的 Cocoa 应用程序来测试它。该应用程序包括 (1) 在新线程上调用 Forker 的 main 方法,(2) 调用 NSApplicationMainForker,以及 (3) 在 NSApplicationMain 完成后获得控制权的窗口控制器它的东西。这3个中的每一个都使用上面提到的方法打印它是否在主线程上。只有 (1) 报告它在主线程上,并且应用程序几乎立即崩溃。正常调用NSApplicationMain时不会崩溃。 那没关系。 @matthias 的回答似乎不错。 【参考方案1】:

Here 是学习 NSApplicationMain 的好地方,可以通过重新实现该函数。 NSApplicationMain 必须从主线程调用,主要是因为

    它处理主接口 UI 元素(在多个系统中,而不仅仅是 OS X)都需要在同一个线程中调用才能正常工作。 Cocoa 框架中提供的图形元素假定它们将在主线程中运行。

差不多了,因为 Cocoa 在主线程中调用事物,并且 UI 都需要在同一个线程中运行,所以您需要在主线程中处理任何与 UI 相关的事情,包括 NSApplicationMain。

【讨论】:

这是一个很好的链接!谢谢你。所有这些事情听起来都是正确的,但是是什么让“主”线程如此特别?为什么没有旧线程?在我看来,只要我将我的应用程序限制在一个线程上,该线程是否是第一个线程就无关紧要了。是否有一些系统调用只能在主线程中工作? (如果是,为什么?) Cocoa 可能使用的其他库是否有此限制?我来自 Java 背景,只要您正确同步,所有线程都是平等的。 从我从 Cocoa 文档中获得的内容来看,只是 Cocoa 假定它位于主线程上,因此可能专门将其图形功能放置在主线程上。我认为至少一些 unix 图形系统没有这个限制(我见过 Qt 讨论图形在子线程上运行),所以它可能是 Cocoa 或其依赖项之一。 从我read 看来,似乎许多平台甚至没有识别主线程的好方法。所以我想最后的答案是:BSD变种可以识别主线程,而Apple决定让这个线程专门用于Cocoa。我对“为什么”的最佳猜测是它有助于强制执行线程限制。我想我们只能忍受它。感谢您的帮助马蒂亚斯! @Calvin — 识别主线程实际上很简单; “主线程”只是程序中第一个线程的另一个名称,即运行_mainmain 的线程。 (所有程序都以单线程开始,并且仅在响应main 或其被调用者中发生的事情时才产生新线程。)因此Apple 的_main 例程可以将其当前线程标记为主线程,它会是正确的。仅仅因为某些平台不向用户公开这些信息并不意味着它们在实现级别没有“主线程”的概念。每个人都有第一个线程。 主线程由 pthreads 实现维护。在 OS X 上,它提供了pthread_main_np() 函数。在各种高级框架中,会参考 pthreads 以使主线程的 CFRunLoopGetMain() 等于 CFRunLoopGetCurrent()。第一个请求主运行循环的线程会导致它被创建并存储在一个共享变量中,但只有主线程从CFRunLoopGetCurrent() 返回该运行循环。等等。因此,尝试在另一个线程上运行 NSApplicationMain() 将无法正常工作,因为该线程不会运行主运行循环。

以上是关于在 Apple 的 Cocoa API 中,为啥从主线程调用 NSApplicationMain 很重要?的主要内容,如果未能解决你的问题,请参考以下文章

Cocoa编码规范

从 Cocoa 应用程序运行 AppleScript

iOS翻译 Cocoa编码规范

私有 API 到底是啥,如果使用了一个 iOS 应用程序,为啥 Apple 会拒绝它?

Cocoa 多窗口拖放示例

如何从 Cocoa 中的应用程序名称中获取 Bundle Identifier?