什么是 PLIST?Apple 的属性列表格式
简短回答:应用程序数据的结构化容器
PLIST 文件——即 Property List(属性列表)的缩写——是 Apple 在其整个生态系统(macOS、iOS、iPadOS、watchOS 和 tvOS)中存储配置设置、偏好设置和序列化数据的首选格式。它的根源可以追溯到 20 世纪 80 年代末的 NeXTSTEP,至今仍是你应用程序记住你选择、系统服务加载配置以及 Xcode 项目描述构建内容的核心支柱。 无论你是否意识到,你的 Mac 上现在就有数百个 PLIST 文件。打开终端并运行 `ls ~/Library/Preferences/`。你会看到一长串文件,例如 `com.apple.finder.plist`、`com.apple.dock.plist` 以及许多其他文件——几乎你用过的每个应用程序都有一个。这些简单的文件告诉 Finder 你的侧边栏应该有多宽,告诉 Dock 你的图标在哪里,并提醒 Safari 你上次退出时打开了哪些标签页。 这种格式支持一小部分固定的数据类型:字符串、整数、浮点数、布尔值、日期、二进制数据 (Data)、数组和字典。这就是全部。没有自定义类型,没有继承,也没有模式。这并非疏忽;而是一种深思熟虑的设计选择。Apple 需要一种操作系统能够以最小开销读写,并且足够简单,以便在手动编辑后不会立即损坏应用程序状态的格式。
XML vs. 二进制 vs. JSON:相同格式的三种风格
尽管 PLIST 格式只有一种,但它有三种不同的编码方式。将它们混淆是一个常见的困惑来源。 首先是 **XML PLIST**,这是人类可读的版本。在文本编辑器中打开一个,你会看到一个 DOCTYPE 声明指向 `http://www.apple.com/DTDs/PropertyList-1.0.dtd`,随后是大量嵌套标签:`<dict>`、`<key>`、`<string>`、`<integer>`、`<true/>`。Xcode 使用它作为其项目文件(`project.pbxproj` 就是其变体),当你需要导出设置进行备份或检查时,这就是你想要的格式。显而易见的缺点是冗长。一个包含 20 个键的简单字典,轻而易举就能扩展到 150 行。 为了性能,macOS 在将文件写入磁盘时几乎总是使用 **二进制 PLIST** (bplist)。当应用程序保存其偏好设置时,它通常会写入一个以魔术字节 `bplist00` 开头的文件。这种格式比其 XML 兄弟文件明显更紧凑,解析速度也快得多。那个 150 行的 XML 文件在二进制形式下可能仅有 400 字节。缺点是,你无法在标准文本编辑器中读取二进制 PLIST 文件;它们看起来只是一堆乱码。 最后是 **JSON PLIST**,这是自 macOS 10.7 以来支持的一个较新的选项。它使用标准的 JSON 语法,但仍然受限于核心 PLIST 数据类型。这个有点特别。JSON 本身不区分整数和浮点数,也没有专门的日期类型,这带来了一些微妙的限制。你很少会看到 Apple 自己的工具生成 JSON PLIST 文件;它们主要出现在第三方构建工具或 CI 流水线生成配置文件时。 幸好,你可以使用 Apple 内置的 `plutil` 命令行工具轻松地在所有三种格式之间进行转换。像 `plutil -convert xml1 com.apple.dock.plist -o dock_readable.plist` 这样的命令可以为你提供一份 Dock 偏好设置的人类可读副本,而无需修改原始二进制文件。
PLIST 文件存储在哪里以及它们控制什么
当你排查行为异常的应用程序或将设置迁移到新机器时,知道 PLIST 文件在哪里就成功了一半。它们存储在几个可预测的位置。 你的个人应用程序设置存储在 `~/Library/Preferences/` 中。你的自定义 Dock 布局、终端配色方案和 Xcode 快捷键绑定都位于此处,并且都与你的特定用户帐户相关联。文件名遵循反向 DNS 命名方案,例如 `com.apple.Terminal.plist` 或 `com.googlecode.iterm2.plist`。相比之下,适用于 Mac 上所有用户的设置则在 `/Library/Preferences/` 中找到。这些文件控制网络配置和时区等系统范围的行为,并且通常需要管理员权限才能修改。 PLIST 文件不仅存储偏好设置;它们还驱动 macOS 自动化系统。`/Library/LaunchAgents/`、`/Library/LaunchDaemons/` 以及用户特定的 `~/Library/` 版本中的文件定义了后台服务和计划任务。一个 LaunchDaemon PLIST 文件告诉 `launchctl` 服务要运行哪个可执行文件、传递哪些参数、崩溃时是否重启以及它的运行计划。 也许最关键的是隐藏在每个 `.app` 包内的 `Info.plist` 文件。在 Finder 中右键点击任何应用程序,选择“显示包内容”,然后导航到 `Contents/Info.plist` 即可查看。这个文件是应用程序的官方“身份证”,声明了它的 Bundle 标识符、最低操作系统版本、所需的硬件能力、URL Scheme 以及所需的权限(例如摄像头或麦克风访问)。在 iOS 上,`Info.plist` 是 App Store 和操作系统本身用来决定你的应用程序是否可以在给定设备上运行的文件。 一个重要的警告:如果你打算编辑 `~/Library/Preferences/` 中的文件,请务必先退出相应的应用程序。在应用程序运行时读取文件没有问题,但如果你写入更改,应用程序在下次保存状态时很可能会覆盖掉你所做的更改。退出应用程序,进行编辑,然后重新启动。
读取和编辑 PLIST 文件:你的实用选项
Apple 提供了一整套工具包来读取和修改 PLIST 文件,从精致的图形界面到强大的命令行工具,应有尽有。 对于大多数开发者来说,**Xcode 内置的 PLIST 编辑器**是最好的起点。它以结构化树状视图打开任何 PLIST 文件,支持类型感知编辑:布尔值有复选框,日期值有日期选择器,数组和字典可以整齐地展开。你可以添加、删除和重新排序键,而无需接触原始 XML。这是编辑 `Info.plist` 和授权文件(entitlement files)的标准、官方认可的工作流程。 对于脚本和自动化,**`plutil` 命令行工具**是不可或缺的。它随 macOS 一起提供,是一个用于验证、转换和键级别编辑的强大工具。`plutil -lint myfile.plist` 可以快速检查语法错误,而像 `plutil -replace NSHighResolutionCapable -bool YES MyApp.app/Contents/Info.plist` 这样的命令可以在无需打开编辑器的情况下设置单个键值。它是 Shell 脚本和 CI/CD 流水线必备的工具。 当你想要更改用户偏好设置时,**`defaults` 命令**是完成这项工作的正确工具。你可以使用 `defaults read com.apple.finder ShowPathbar` 读取当前设置,或者使用 `defaults write com.apple.finder ShowPathbar -bool TRUE` 更改它。这正是为什么许多 macOS“高级用户”的自定义设置都以简单的 `defaults write` 一行命令形式分享的原因。 有时你需要更强大的功能。**第三方编辑器**,如 PlistEdit Pro(截至 2025 年,在 Mac App Store 上售价约为 12 美元),增加了 Xcode 缺乏的功能,例如并排比较、无需转换直接编辑二进制 PLIST,以及批量操作。如果你发现自己每天都在与 PLIST 文件打交道,那么投资一个专用工具是明智的选择。 那么简单的**文本编辑器**呢?它对 XML PLIST 文件完美适用,但会损坏二进制文件。如果你在 VS Code 或 BBEdit 中打开二进制文件,你必须首先使用 `plutil -convert xml1` 将其转换为 XML。编辑完成后,在系统使用它之前,再使用 `plutil -convert binary1` 将其转换回去。
转换 PLIST 文件:CocoConvert 能做什么和不能做什么
CocoConvert 旨在处理人们在网络上遇到的最常见的 PLIST 转换场景:将 XML PLIST 转换为 JSON,将 JSON 转换为 XML PLIST,以及无需开发者工具即可将二进制 PLIST 文件解码为可读的 XML。 对于 XML 到 JSON 的转换,CocoConvert 将 PLIST 数据类型映射到其对应的 JSON 类型。字符串、整数、数组和字典可以干净地转换。布尔值变为 JSON 的 `true` 和 `false`。日期被序列化为标准的 ISO 8601 字符串(例如,`2024-11-03T14:22:00Z`)。来自 `<data>` 元素的任何二进制数据都会在输出中进行 base64 编码,这完美地保留了内容,但也意味着 JSON 中的这些特定字段将不可读。 二进制到 XML 的功能特别有用。如果你曾使用第三方工具从 iPhone 导出了偏好设置备份,CocoConvert 可以解析生成的 `bplist` 文件并生成可读的 XML PLIST,让你无需在机器上安装 Xcode 即可检查其内容。 我们还需要明确 CocoConvert 不能做什么:它无法将 PLIST 文件转换回二进制格式。生成二进制 PLIST 需要构建精确的字节级偏移表,这项任务对 Apple 的原生库来说很简单,但在网络服务中正确实现却非常困难。如果你需要将修改后的二进制 PLIST 写回设备——例如,恢复编辑过的 iPhone 偏好设置——你必须在 Mac 上使用 `plutil` 或像 PlistEdit Pro 这样的原生编辑器。虽然 macOS 通常可以在预期二进制文件的地方读取 XML 文件,但有些应用程序很严格,会拒绝或忽略 XML 版本。 CocoConvert 也只验证结构,不验证语义。一个 Bundle 标识符格式错误或操作系统版本无效的 PLIST 文件也能正常转换,因为从文件格式的角度来看,它是有效的。这些是应用程序层面的问题,格式转换器无法诊断。
常见的 PLIST 问题及诊断方法
PLIST 损坏很少见,但这是一种特别令人沮丧的 macOS 故障排除场景。症状——应用程序无法启动、偏好设置在每次重启后重置、系统服务静默失败——很少直接指向某个特定文件。 最常见的原因是**写入不当造成的损坏**。如果 macOS 在应用程序保存设置时崩溃或断电,磁盘上的 PLIST 文件可能会被截断或乱码。你的第一个诊断步骤应该是 `plutil -lint ~/Library/Preferences/com.example.app.plist`。健康的文件会返回 `OK`;损坏的文件会给出解析错误,通常会附带一个有用的行号或字节偏移量。 **权限问题**紧随其后。用户 `~/Library/Preferences/` 目录中,如果某个 PLIST 文件莫名其妙地被 `root` 拥有,会导致应用程序在每次启动时都静默地回退到其默认设置。使用 `ls -l ~/Library/Preferences/com.example.app.plist` 检查所有权——所有者必须是你的用户名,而不是 `root`。你可以使用 `sudo chown $(whoami) ~/Library/Preferences/com.example.app.plist` 来修复它。 一个更微妙的问题是**缓存偏好设置**。为了提高速度,macOS 使用一个后台守护进程 `cfprefsd` 来缓存偏好设置值。这意味着即使你直接编辑磁盘上的 PLIST 文件,正在运行的应用程序也可能继续读取旧的缓存版本。如果你的 `defaults write` 更改没有生效,这几乎肯定是原因所在。使用 `killall cfprefsd` 强制刷新缓存(它会自动重启),或者简单地注销并重新登录。 Xcode 构建失败通常可以追溯到格式错误的 `Info.plist`。构建会失败,并显示一个模糊的错误,例如“无法读取数据,因为它不是正确的格式”,这只是 Xcode 表示文件解析失败的方式。在你做任何其他事情之前,请检查 XML 中是否存在类似 `<<<<<<<` 的合并冲突标记,或者直接对文件运行 `plutil -lint`。 对于任何非你自己创建的 PLIST 文件——无论是来自设备备份、GitHub 仓库还是同事的文件——请务必先对其运行 `plutil -lint`。这只需要三秒钟,却能省去无数的困惑。
PLIST 在更广泛的 Apple 开发工作流程中
除了简单地存储偏好设置,PLIST 文件还是 Apple 开发工具链中的承重基础设施,其重要性往往只有在出现问题时才变得显而易见。 应用程序的代码签名依赖于一个 entitlements PLIST 文件。这个文件精确声明了应用程序被允许使用哪些特殊能力:iCloud、推送通知、App Groups、Keychain 共享等等。在构建过程中,这个文件会直接嵌入到应用程序的代码签名中。如果 entitlements PLIST 与配置描述文件不匹配,应用程序就无法安装到设备上。Apple 的 `codesign` 工具直接读取并验证此文件。 Xcode 的构建系统本身也严重依赖 PLIST 文件。在 `.xcodeproj/xcshareddata/xcschemes/` 中找到的 `*.xcscheme` 文件就是 PLIST 文件,它们描述了要构建哪些目标、启动时传递哪些参数以及设置哪些环境变量。由于它们只是结构化的 XML,因此可以安全地提交到版本控制中,并且易于在不同分支之间进行比较 (diff)。 甚至 App Store 提交的元数据也由 PLIST 文件管理。隐私清单 (`PrivacyInfo.xcprivacy`),在 Xcode 15 中引入,并要求在 2024 年 5 月之后的大多数应用程序提交中提供,它就是一个 PLIST 文件。它声明了你的应用程序使用了哪些可能用于设备指纹识别的 API,以及原因。弄错这个文件不会导致构建错误;它会导致 App Store 审核被拒,这在调试时会更令人恼火得多。 对于任何构建与 Apple 生态系统交互的跨平台工具的人来说——无论是 CI 系统、MDM 解决方案还是备份工具——深入理解 PLIST 格式都是不可避免的。格式规范记录在 Apple 的 `CFPropertyList` 参考文档中,但二进制格式也已被彻底逆向工程。Python、Ruby、Go 和 Rust 都有优秀的开源解析器。Python 标准库中的 `plistlib` 模块(自 3.4 版本起)对于需要处理设备备份或 Xcode 项目文件的生产脚本来说尤其可靠。