在 macOS 上进行日常办公和开发时,我们常常会遇到需要重复性操作的场景。作为 macOS 历史悠久的自动化方案,AppleScript 是许多效率极客和开发者的首选。
但大家是否好奇过,AppleScript 到底能做到什么程度?在它那有些“奇特”的自然语言语法背后,是否有更底层的系统 API 在支撑着这一切?今天我们就来深入聊聊 macOS 的自动化底座。
AppleScript 的核心功能
AppleScript 诞生于 1993 年的 System 7,它的设计初衷是为了让没有编程经验的普通用户也能轻松控制电脑。它的核心能力可以概括为以下几个方面:
1. 应用程序间通信 (IAC)
AppleScript 最强大的地方在于它能够直接调用支持 Apple Events 的应用程序的内部对象和属性。比如,我们可以让 Finder 创建一个文件夹,让 Safari 打开一个特定网页,或者让 Music 播放特定曲目。
2. UI 脚本自动化 (UI Scripting)
对于那些没有提供完善 AppleScript 字典的应用程序,AppleScript 可以通过 System Events 进程来强行模拟用户的操作。这就包括了:
- 点击屏幕上的任意菜单栏按钮。
- 模拟键盘敲击(如
keystroke "v" using {command down})。 - 查找特定的窗口并将其置于前台。 在我们实际的项目开发中(比如控制微信公众号后台),由于网页内部无法直接通过 Apple Event 操控,我们常常利用 AppleScript 完成浏览器窗口的前置和剪贴板的快捷键粘贴。
3. 系统级控制
AppleScript 还可以执行系统级的命令,比如休眠、重启、调整音量、读写剪贴板(通过 the clipboard 对象),甚至执行底层的 Shell 脚本(通过 do shell script 命令)。
突破瓶颈:macOS 还有更底层的 API 吗?
尽管 AppleScript 看起来无所不能,但它也有明显的短板:语法晦涩难懂、执行效率偏低、调试困难。
当我们的自动化需求变得更加复杂和对性能敏感时,我们可能会问:AppleScript 底层调用了什么?我们能直接调用那些更底层的 API 吗?
答案是肯定的。macOS 提供了一套非常严密的底层框架供开发者使用。
1. Apple Events API (C/Objective-C)
AppleScript 的所有对象调用,底层本质上都在发送 Apple Events(一种进程间通信协议)。如果我们觉得 AppleScript 太慢,我们可以直接在 C 或 Objective-C 中使用 <Carbon/Carbon.h> 中的 NSAppleEventDescriptor 等 API,手动构建和发送 Apple Events。这样能省去 AppleScript 脚本解释器的开销。
2. Accessibility API (AX API)
前面提到的“UI 脚本”模拟点击,其底层支撑是 macOS 的 Accessibility API。
它最初是为视障用户提供辅助功能(如 VoiceOver)设计的,但它允许程序读取和操作其他应用程序的 UI 元素树。
如果我们使用 Swift 或 Objective-C 开发,可以直接使用 AXUIElement 相关的底层 C API。我们会发现,使用底层 AX API 查找一个窗口的坐标并点击,比 AppleScript 的 System Events 要快几个数量级!
3. JXA (JavaScript for Automation)
这是 Apple 在 Yosemite (macOS 10.10) 中引入的机制。如果我们受够了 AppleScript 奇怪的语法,我们可以直接使用 JavaScript 来编写自动化脚本。它和 AppleScript 共享同一个底层执行引擎(OSA - Open Scripting Architecture),因此能调用同样的应用程序字典,但语法更现代。
4. 现代进程通信:XPC
对于现代的 macOS 应用程序开发,Apple 正在大力推广 XPC 机制,用于应用内部进程间或应用间的安全通信。相比于古老的 Apple Events,XPC 速度更快,权限控制更严格,且完美融入了现代 Swift/Objective-C 的异步并发模型。
总结
在 macOS 上做自动化,我们其实是在不同的抽象层级中做出选择:
- 快速验证和脚本拼凑:毫不犹豫地选择 AppleScript 或 JXA。
- 极致的性能和深度的 UI 操控:使用 Swift/Objective-C 直接调用 Accessibility API。
- 现代应用架构:采用 XPC 等框架构建稳健的后台进程。
了解了这背后的 API 层级,我们就能在面对复杂的自动化需求时,选择最趁手的兵器。