macOS XPC 上手

XPC 是 macOS 里经常用到的进程间通信机制。本文不关注 XPC 的内部机制,而是一篇教你如何上手 XPC 的文章。

  1. 创建 macOS 工程 FRTestXPC,选择 Cocoa App Template

  2. 在工程 FRTestXPC 里点击增加 Target,在弹出框搜索 XPC, 选择搜索结果中的 XPC Service,然后命名 TargetFRTestXPC_Helper 并将 target 对应的 info.plist 文件中 bundle id 修改为 'com.frank.xpchelper'

  3. 选择 FRTestXPC Target -> Build Phases 然后新增 CopyFiles 项目,将刚刚的 FRTestXPC_Helper.xpc 加进去,注意 Destination 必须要选择 XPC Services。否则后续主程序和 XPC 会无法建立连接。

  4. 回过头来看工程目录里 FRTestXPC_Helper -> main.m 文件,这里 main 函数是 XPC 程序的入口函数。可以看到 Xcode 已经帮助生成很多 demo code。我们已经不需要做太多。

  5. 在主程序的窗口里增加一个 button,在点击方法里实现如下代码

    NSXPCConnection *con = [[NSXPCConnection alloc]     initWithServiceName:@"com.frank.xpchelper"];
    NSXPCInterface *remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(FRTestXPC_HelperProtocol)];
    con.remoteObjectInterface = remoteInterface;
    [[con remoteObjectProxy]  upperCaseString:@"hello" withReply:^(NSString *reply) {
    NSLog(@"reply = %@",reply);
        }];
    [con resume];
    
  6. 启动程序,点击 button,观察终端输出为 HELLO

# 一些说明

  1. Tips :作为 demo 可以删除掉 Project 里的各种 Test Target 和 Test 文件,保持工程清爽直观。
  2. 我用的是 Xcode9.1版本,创建好的 XPC 中的代码完全为 Xcode 自动生成的,(void)upperCaseString:(NSString *)aString withReply:(void (^)(NSString *))reply; 这个协议也是 Xcode 生成的,所以看上面代码不要太突兀,你自己试试就知道了。
  3. 初始化主程序 NSXPCConnection 的时候必须写正确 XPC 的 service name.

# 一些 QA

Q: 什么场景下会用到 XPC? 具体到项目中说一下。 A: 我觉得用到 XPC 的地方主要还是希望让别的进程分担自己的任务,同时又能够及时的和主进程进行通信。(比如一些网络处理的服务)

Apple WWDC 的讲解也是这么说的 So if you just have some background piece of work that needs to happen every so often, you can use XPC to talk to it, initiate it, and get it running.

其次是考虑一些其他因素,比如

  • 增加程序稳定性(XPC Service crash 但主程序不 crash).
  • XPC 服务处理一些不够安全的东西,这时候我们不提供 XPC 任何权限,比如网络访问,文件访问等,但是主程序还是保留所有的权限。 相当于让小弟干活儿,但是不给小弟太多权利,干的了就干,干不了就别干…..
  • 很重要一点:是这是 App Store 允许的多进程的方式。

Q: XPC 服务有界面吗? A: 一般都是后台进程。没见过有人用 XPC 搭建界面的。

Q: 系统是怎么通过以下这些 API 方法找到 XPC 服务的 ?

NSXPCInterface *myCookieInterface = [NSXPCInterfaceinterfaceWithProtocol:@protocol(XPC_HelperProtocol)];
NSXPCConnection *myConnection = [[NSXPCConnection alloc]    initWithServiceName:@"com.frank.xpchelper"];

A: 当 app 启动的时候,系统自动注册 app 中可见的 XPC 服务,当 app 与某个 XPC 服务建立连接,然后向他发送包含事件服务的消息。

When the app is launched, the system automatically registers each XPC service it finds into the namespace visible to the app. An app establishes a connection with one of its XPC services and sends it messages containing events that the service then handles

# 一个 Demo

以上的 demo 是最简单的,一般 XPC 用来做后台下载处理比较合适,所以写了一个比上面代码复杂一点点(不过还是很简单)的 demo 放到 github 上,这个 demo 是 XPC 双向同步的。即 XPC Service 可以不依赖于代理方法的回调而是直接给 Client app 发送消息。

NSXPCConnection 是最主要的 API,其余的 API 比如 NSXPCInterface,都是依附于 Connection。构建双向通信的 XPC Demo 在 ClientApp 和 XPC 里有一些代码非常相似。即互相暴露接口,互相暴露交接的对象。Demo 也提供了一些非常实用的构建 XPC 时用的 API。

Demo 地址: https://github.com/fanxiushan/Demo.XPCTestDemo

# 参考地址

Creating XPC Services (opens new window) Mac Technology Overview -> Kernel and Device Drivers Layer -> High-Level Features -> XPC Interprocess Communication and Services (opens new window) WWDC (opens new window) XPC (opens new window)