iOS系统是出了名的封闭,每个应用的活动范围被严格地限制在各自的沙盒中。尽管如此,iOS还是提供了若干进程间通信机制,CFMessagePort就是其中之一。
从类名可以看出,CFMessagePort属于Core Foundation
层的东西,其实现部分是开源的,代码在可以在苹果的开源代码库中找到。
CFMessagePort端口消息的接收者需要实现以下功能:
消息接收者需要通过以下方式注册消息监听:
1 2 3 4 5 6 7 8 9 10 11 |
|
其中LOCAL_MACH_PORT_NAME
的定义为:
1
|
|
经过查看源码发现,CFMessagePort实际上是通过mach port实现的。Mach port是iOS系统提供的基于端口的输入源,可用于线程或进程间通讯。而Runloop支持的输入源类型中就包括基于端口的输入源,因此可以使用Runloop做为CFMessagePort端口源事件的监听者。
上述代码有几点需要说明:
通过CFMessagePortCreateLocal可以创建一个本地CFMessagePortRef对象
CFMessagePort对象是靠一个字符串来唯一标识的,这一点非常重要,在这里字符串是由宏LOCAL_MACH_PORT_NAME
定义的;
创建CFMessagePort对象的同时设置了端口源事件的回调函数onRecvMessageCallBack,用于处理端口源事件;
将创建的对象作为输入源添加到Runloop中,从而实现对端口源事件的监听,当Runloop收到对应的端口源事件时,会调用上一步中指定的回调芳芳;
回调函数为CFMessagePortCallBack类型,其定义部分为:
1 2 3 4 5 6 |
|
各个参数的含义为:
CFMessagePortRef local
当前接收消息的CFMessagePortRef对象。
SInt32 msgid
这个字段非常有用,用于标识消息。如果通信双方进程约定号每个msgid对应的数据结构,即可实现较为复杂的通信。
CFDataRef data
通信的真正数据部分。
void *info
为使用CFMessagePortCreateLocal方法创建port端口时指定的CFMessagePortContext对象的info字段,通常为空。
该回调方法可以返回一个CFDataRef类型的数据给port消息的发送者,以实现有效的双方通信,这一点也非常重要。
我的回调函数onRecvMessageCallBack的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
该方法实现的较为简单,解析约定的数据(测试代码中约定传送的是string),为了测试,同时生成一个CFDataRef数据返回给port消息的发送者。
可以通过如下方式取消对port端口的监听:
1 2 3 4 5 |
|
CFMessagePortInvalidate会停止port消息的发送和接收操作,而只有调用了CFRelease,CFMessagePortRef对象才真正的被释放掉。
发送部分代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
其中MACH_PORT_REMOTE
的定义为:
1
|
|
发送消息时要相对简单,首先通过CFMessagePortCreateRemote生成一个Remote的CFMessagePortRef,这里需要注意的是CFMessagePortCreateRemote时传入的字符串唯一标识MACH_PORT_REMOTE
必须和消息接收者创建local的CFMessagePortRef时使用的字符串唯一标识是同一个!
通过查看源码发现,CFMessagePortCreateRemote会根据MACH_PORT_REMOTE
定义的字符串为唯一标识获取消息接收者通过CFMessagePortCreateLocal使用相同字符串创建的底层mach port端口,从而实现向消息接收者发送信息。
如果消息接收者还没有创建或者通过CFMessagePortCreateLocal创建local端口失败时,想要通过CFMessagePortCreateRemote去创建remote端口肯定是失败的。
在使用CFMessagePortCreateLocal/CFMessagePortCreateRemote创建CFMessagePortRef对象时会失败,官方文档中是这么说的:
1
|
|
CFMessagePort只能用于本地进程通信。
CFMessagePort是基于mach port端口的通信方式,不但可以用于进程通信,也可以用于线程间通信,只是线程间通信有了GCD和Cocoa提供的原生方法,已经能很方便的实现了,没必要再使用CFMessagePort。
进程通信使用场景
iOS系统多任务机制,使得进程间通信基本都只能用于越狱开发。常用的场景是前端有一个UI程序用于界面展示,后端有一个daemo精灵程序用于任务处理。
特地做了了个demo工程,以便更好地演示CFMessagePort的使用,可以到CSDN下载。
为了模拟进程间通信场景,我将消息接收进程CFMessagePortReceive做成了能够后台播放音乐的程序,以便其切到后台后能继续存活。
由于CFMessagePort不再支持iOS7及以后系统,本demo实在iOS6系统上测试的。
demo使用方式:
CFMessagePortReceive启动后,点击Start Listenning创建CFMessagePort接口并开始监听port消息,然后将CFMessagePortReceive切到后台;
启动CFMessagePortSend程序,在输入框中写入内容,点击发送按钮即可和CFMessagePortReceive通信。
MessagePort通信过程中会有日志输出,可以使用以下方式查看日志:
1.真机
选择:Xcode->Window->Organizer->Devices,然后选中窗口左侧当前设备的Console窗口查看。
2.模拟器
选择:模拟器->调试->打开系统日志,或者直接使用快捷键⌘/
直接打开系统控制台查看日志。