macOS上的进程间通信的方案很多,总的来说分为以下三种,如下图所示

IPC

  • 第一种也是历史最悠久的是基于POSIX的实现,其中常见的是基于Socket (TCP/IP)的实现。同时这种方案和平台无关。
  • 第二种基于 底层信号机制,mac系统的内核组件就是使用mach_mag来传递消息。参考
  • 第三种就是Foundation库里提供的一些类库。github上有个不错的范例实现基于CFMachPort—https://github.com/Syphon/Simple​

XPC方式

估计是苹果觉得这些方案,都不够好,要么不够易用,要么不够安全。所以在2011年的时候推出的全新的XPC进程间通信的方案。

The XPC Services API provides a low-level (libSystem) interprocess communication mechanism based on serialized property lists.

这是一套C的API,范例代码可以参考https://github.com/objcio/issue-14-xpc

但是这个技术有一个挺大的局限性,XPC进程只能是一个不带界面的控制台进程。关于 XPC 比较特别的尝试可以看看这个代码: https://github.com/OpenEmu/OpenEmuXPCCommunicator

这可能一般的简单的程序架构是没问题的,但是有些特殊的要求,比如我就要求主程序和子程序都是界面程序 并且可以互相调用,这样子就完全搞不定了,由于XPC这套东西并不开源,所以要实现这种更灵活的互相调用,只能另辟蹊径。

为了实现这种两个界面程序,主程序和子程序可以互相调用的需求推荐就采取 Socket的方式实现,因为这种最标准的方式和操作系统无关,相比其他方案 不会因为 系统API被废弃而 变得不可用。

Socket方式

一个理想的调用关系大概是这样的:

子程序放在主程序的程序包内。

当主程序需要调用子程序时 主程序先创建一个 TCP Server,并且把Server的地址和端口 作为参数 调起子程序(NSTask)。

子程序启动后,获取参数中的TCP地址和端口, 然后创建TCP Client进行连接。然后通过TCP的通信来互相调用。

至此 通信层的功能和大体框架基本确定了。

那应用层应该以何种方式调用呢?是否要定义一个特别的通信规约?

如果定义了通信规约,那每次别人要使用这套框架的时候还要先熟悉一下规约,这就显得不灵活 和 不易用了。有没有可能借用XPC的思路,直接以Objc消息传递的方式来互相调用呢?借助于Objective-C的runtime,这是可以实现的。我个人XPC的内部实现思路估计也是一样的。

先复习一下Objc的消息转发流程:

注意最后一步 forwardInvocation 可以获取函数的所有调用参数。

-(void)forwardInvocation:(NSInvocation *)anInvocation;

假如 主程序利用 forwardInvocation 把需要调用的子程序的 函数信息 序列化 通过 Tcp 传递给子程序,子程序 反序列化后再根据函数信息调用对应功能。这就完成了一个互相调用的过程。

实现对应功能的时候注意一下 NSInvocation 在Swift已经被废弃了,所以相关功能只能使用OC编写。

另外 NSInvocation 在ARC环境下跑的时候有个坑,可以参考一下 nsinvocation-and-arc-automatic-reference-counting

具体的代码就不放上来,大家有兴趣可以按照上述思路 实现一下看看,一方面可以熟悉一下TCP流程,另一方面也可以熟悉 OC的 转发流程,另外实现完这一套之后 也会 对苹果XPC内部实现有一定的了解。

参考链接: