macOS 应用的解剖结构
The Anatomy of a macOS App

原始链接: https://eclecticlight.co/2025/12/04/the-anatomy-of-a-macos-app/

## macOS 应用结构:历史概述 早期的 Mac 应用(如 QuarkXPress 4.11)将资源——窗口、菜单等——存储在与可执行代码分离的“资源分支”中。这与简单的命令行程序形成对比。Mac OS X 的出现带来了从 NeXTSTEP 继承的重大转变,即“捆绑包”结构。 现代 macOS 应用本质上是结尾为 `.app` 的目录,包含一个 `Contents` 文件夹。该文件夹包含可执行代码(在 `MacOS` 中)、资源(如图标,在 `Resources` 中)以及潜在的共享库(`Frameworks`)。 重要的是,`Info.plist` 定义了应用元数据——可执行文件名称、所需的 macOS 版本、文档类型——而 `PkgInfo` 保留了经典的 Mac OS 类型/创建者信息。 启动应用涉及 `launchd`、`LaunchServices` 和 `RunningBoard`,它们都依赖于 `Info.plist` 数据。 随着时间的推移,安全功能(如代码签名,Leopard 中添加)和公证引入了 `_CodeSignature` 和 `_MASReceipt` 等目录。 如今的捆绑包越来越集中组件——以前分散在 Library 文件夹中——在应用本身内(例如,`Library`、`XPCServices`、`Plugins`)。这种自包含的方法简化了安装、更新、删除,并通过签名和公证增强了安全性,无论 Intel 还是 Arm 架构。

## macOS 应用解剖与开发者困境 一篇最近在 Hacker News 上的帖子剖析了 macOS 应用程序的结构,突出了开发者面临的复杂性。虽然核心结构相对标准,但现代 macOS 的要求,例如公证,增加了显著的障碍。 公证,旨在提高安全性,实际上是强制性的,因为未公证的应用会向用户触发严重警告,需要导航系统设置才能运行。开发者们表达了担忧,认为公证并未明显提高安全性,反而增加了每年 99 美元的成本。与 Windows 和 iOS 的比较显示出类似的“付费才能玩”的情况,Windows 代码签名在历史上成本更高,但最近通过 Azure 代码签名得到了改善。iOS 应用分发也面临着测试要求的挑战。 讨论揭示了开发者对苹果控制和缺乏透明度的沮丧,尤其是在验证流程和证书吊销方面。一些开发者选择使用 Web 应用来避免平台特定的麻烦,而另一些开发者则强调 macOS 标准化结构的优势,尽管存在额外的摩擦。最终,这场对话强调了一种日益增长的观点,即平台限制正在阻碍开发者,并可能扼杀创新。
相关文章

原文

Programs running in windowing environments, applications as we used to know them, have more complicated requirements than those run from a command line. Rather than embed all the resources they require for windows, menus and the rest in a single file, Mac OS broke new ground by putting those into resources stored in the app’s resource fork.

prefsresedit

This is QuarkXPress version 4.11 from around 2000, with its resources displayed in the resource editor ResEdit. Executable code was also stored in CODE resources, and every file contained type and creator information to support the illusions created by the Finder.

Mac OS X

When Mac OS X was designed, it switched to the bundle structure inherited from NeXTSTEP. Instead of this multitude of resources, apps consisted of a hierarchy of directories containing files of executable code, and those with what had in Mac OS been supporting resources. Those app bundles came to adopt a standard form, shown below.

The bundle name has the extension .app, and contains a single directory Contents. Within that, the executable code is in the MacOS directory, which may contain both the main executable for the GUI app and any bundled command tools provided. Another directory contains Resources, including the app’s custom icon, and components of its GUI. In some apps, there’s another directory of Frameworks containing dylibs (libraries).

There are also two important files, Info.plist and PkgInfo. The latter contains the same type and creator information inherited from Classic Mac OS, and apparently isn’t mandatory although it appears universal. The information property list is essential, as it specifies the names of the executable and its icon file in Resources, the minimum version of macOS required, type declarations of the app’s documents, version numbers, and more.

When running a command tool in macOS, its Mach-O executable is launched by launchd, whose purpose is to run code. Launching an app is more demanding, although the app’s executable is still launched by launchd. Before that can happen, macOS starts the launch process using LaunchServices and RunningBoard, which rely on information obtained from Info.plist and other components in the app bundle.

macOS

This structure remained stable until the introduction of code signatures in Mac OS X 10.5 Leopard in 2007. Accommodating those added a directory named _CodeSignature containing the signature in a CodeResources file. That includes code directory hashes (CDHashes) to check the integrity of the contents of the app bundle. Apps distributed by the App Store include a store receipt in another directory, _MASReceipt. Since 2018, when Apple introduced notarization, the ‘ticket’ issued by Apple can be ‘stapled’ into the app bundle as the file CodeResources.

Many apps come with additional items that might in the past have been installed by them in their Library/Application Support folders and elsewhere, but are now included in the app bundle. These can include the following directories:

  • Library, containing folders of LaunchDaemons and LoginItems that would previously have been installed in either the main Library folder, or that in the user’s Home folder;
  • XPCServices, for executable code that the app uses to provide specific services;
  • Plugins, for some types of app extension (Appex);
  • Extensions, for other types of app extension, including app intents.

You may also come across other components, including a version.plist in Apple’s apps.

This centralisation of components in the app bundle has brought several benefits. Being self-contained, apps are easier to install and update, and cleaner to remove. Their components are less likely to go missing, and most of all they’re held within the protection of the app’s signature and notarisation, an important improvement in security.

Assembling these into a diagram shows how the anatomy of an app has grown over the last few years.

Components shown in pale yellow are either mandatory or essentially universal. Those shown in green are found in apps distributed through the App Store, while that shown in blue is the stapled notarisation ticket (optional). You will also see additional folders and components such as Automator workflows, scripts, and others.

There is no difference in structure between apps built for current Intel and Arm architectures. That’s because binaries in the MacOS folder (and executable code in other directories like Frameworks, XPCServices and Plugins) contain platform-specific code in a single Mach-O executable. Thus, an app that’s Universal and runs native on both architectures includes code for both in its single ‘fat’ code file, and they even have separate signatures stored within common files.

联系我们 contact @ memedata.com