IOS基础:RunLoop(下)
原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 一、RunLoop
- 1、概念
- 2、作用
- 3、结构
- 4、原理
- 5、循环流程
- 6、Runloop与线程之间的关系
- 7、答疑
- 二、Model
- 1、CFRunLoopModelRef
- 2、CFRunLoopSourceRef
- 3、CFRunLoopTimerRef
- 4、CFRunLoopObserverRef
- 三、RunLoop实际运用
- 1、NSTimer
- 2、GCD Timer
- 3、CADisplayLink
- 4、AutoreleasePool
- 5、事件响应
- 6、手势识别
- 7、界面更新
- 8、AFNetworking的常驻线程
- 9、滚动Scrollview导致定时器失效
- 10、图片下载后延迟显示
- 11、观察事件状态,优化性能
- 12、线程间通信
- 13、AsyncDisplayKit
- 四、RunLoop实现原理
- 1、获取RunLoop
- 2、添加Mode
- 3、添加Run Loop Source(ModeItem)
- 4、添加Observer和Timer
- 五、RunLoop运行原理
- 1、CFRunLoopRun
- 2、CFRunLoopRunInMode
- 3、CFRunLoopRunSpecific
- 4、__CFRunLoopServiceMachPort
- Demo
- 参考文献
续文见上篇 IOS基础:RunLoop(上)
四、RunLoop实现原理
RunLoop
对象包括Fundation
中的NSRunLoop
对象和CoreFoundation
中的CFRunLoopRef
对象。因为Fundation
框架是基于CoreFoundation
的封装,因此我们学习RunLoop
还是要研究CFRunLoopRef
源码。
1、获取RunLoop
苹果官方对于 RunLoop
的创建进行了封装,也就是说我们找不到像alloc
或者new
这样的方法去手动创建RunLoop
。要获取主线程或当前线程对应的 RunLoop
,只能通过CFRunLoopGetMain
或 CFRunLoopGetCurrent
函数。获取方法如下:
//Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
//Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
a、CFRunLoopGetCurrent
在CFRunLoopGetCurrent
函数内部调用了_CFRunLoopGet0()
,传入的参数是当前线程pthread_self()
。可以看出,CFRunLoopGetCurrent
函数必须要在线程内部调用,才能获取当前线程的RunLoop
。也就是说子线程的RunLoop
必须要在子线程内部获取。
// 获取当前线程的 RunLoop
CFRunLoopRef CFRunLoopGetCurrent(void) {
return _CFRunLoopGet0(pthread_self());
}
b、CFRunLoopGetMain
在CFRunLoopGetMain
函数内部也调用了_CFRunLoopGet0()
,传入的参数是主线程pthread_main_thread_np()
。可以看出,CFRunLoopGetMain()
不管在主线程还是子线程中调用,都可以获取到主线程的RunLoop
。
//取主线程的RunLoop
CFRunLoopRef CFRunLoopGetMain(void) {
static CFRunLoopRef __main = NULL; // no retain needed
//传入主线程
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
c、CFRunLoopGet0
前面两个函数都是使用了CFRunLoopGet0
实现传入线程的函数,分析CFRunLoopGet0
源码可以得出以下结论。
-
RunLoop
和线程的一一对应的,对应的方式是以key-value
的方式保存在一个全局字典中 - 主线程的
RunLoop
会在初始化全局字典时创建 - 子线程的
RunLoop
会在第一次获取的时候创建,如果不获取的话就一直不会被创建,与懒加载很相似 -
RunLoop
会在线程销毁时销毁
// 全局的 dictionary, key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFSpinLock_t loopsLock = CFSpinLockInit;
// t==0 is a synonym for "main thread" that always works
//根据线程取RunLoop
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
//如果存储RunLoop的字典不存在,即当第一次进入时,创建全局 dictionary
if (!__CFRunLoops) {
__CFSpinUnlock(&loopsLock);
//创建一个临时字典dict
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//创建主线程的RunLoop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//把主线程的RunLoop保存到dict中,key是线程,value是RunLoop
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
//此处NULL和__CFRunLoops指针都指向NULL,匹配,所以将dict写到__CFRunLoops
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
//释放dict
CFRelease(dict);
}
//释放mainrunloop
CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}
//以上说明,第一次进来的时候,不管是getMainRunloop还是get子线程的runloop,主线程的runloop总是会被创建
//用传进来的线程作key,从字典__CFRunLoops中获取传入线程t的runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
//如果没有获取到线程t的runloop
if (!loop) {
//那么就根据线程t创建一个runloop
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//把newLoop存入字典__CFRunLoops,key是线程t
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
__CFSpinUnlock(&loopsLock);
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
CFRelease(newLoop);
}
//如果传入线程就是当前线程
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
//注册一个回调,当线程销毁时,销毁对应的RunLoop
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
// 获取主线程的 RunLoop
CFRunLoopRef CFRunLoopGetMain(void) {
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np());
return __main;
}
}
2、添加Mode
a、CFRunLoopAddCommonMode
没有办法直接创建一个CFRunLoopMode
对象,但是我们可以调用CFRunLoopAddCommonMode
传入一个字符串向RunLoop
中添加Mode
,传入的字符串即为Mode
的名字,Mode
对象应该是此时在RunLoop
内部创建的。分析CFRunLoopAddCommonMode
源码可以得处以下结论:
-
modeName
不能重复。modeName
是mode
的唯一标识符 -
RunLoop
的_commonModes
数组存放所有被标记为common
的mode
的名称 - 添加
commonMode
会把commonModeItems
数组中的所有source
同步到新添加的mode
中 -
CFRunLoopMode
对象在CFRunLoopAddItemsToCommonMode
函数中调用CFRunLoopFindMode
时被创建
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
__CFRunLoopLock(rl);
//看rl中是否已经有这个mode,如果有就什么都不做
if (!CFSetContainsValue(rl->_commonModes, modeName)) {
CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL;
//把modeName添加到RunLoop的_commonModes中
CFSetAddValue(rl->_commonModes, modeName);
if (NULL != set) {
CFTypeRef context[2] = {rl, modeName};
//add all common-modes items to new mode
//CFRunLoopAddSource/CFRunLoopAddObserver/CFRunLoopAddTimer的时候会调用
//这里调用__CFRunLoopFindMode(rl, modeName, true),CFRunLoopMode对象在这个时候被创建
CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context);
CFRelease(set);
}
} else {
}
__CFRunLoopUnlock(rl);
}
b、CFRunLoopAddCommonMode
直接取RunLoop
的_currentMode
和_modes
返回。
3、添加Run Loop Source(ModeItem)
a、CFRunLoopAddSource
- 如果
modeName
传入kCFRunLoopCommonModes
,则该source
会被保存到RunLoop
的_commonModeItems
中,且会被添加到所有commonMode
中 - 如果
modeName
传入的不是kCFRunLoopCommonModes
,则会先查找该Mode
,如果没有,会创建一个 - 同一个
source
在一个mode
中只能被添加一次
//添加source事件
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) {
__CFRunLoopLock(rl);
//如果是kCFRunLoopCommonModes
if (modeName == kCFRunLoopCommonModes) {
//如果runloop的_commonModes存在,则copy一个新的复制给set
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
//如果runl _commonModeItems为空
if (NULL == rl->_commonModeItems) {
//先初始化
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
//把传入的CFRunLoopSourceRef加入_commonModeItems
CFSetAddValue(rl->_commonModeItems, rls);
//如果刚才set copy到的数组里有数据
if (NULL != set) {
CFTypeRef context[2] = {rl, rls};
/* add new item to all common-modes */
//则把set里的所有mode都执行一遍__CFRunLoopAddItemToCommonModes函数
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
//以上分支的逻辑就是,如果你往kCFRunLoopCommonModes里面添加一个source,那么所有_commonModes里的mode都会添加这个source
} else {
//根据modeName查找mode
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
//如果_sources0不存在,则初始化_sources0,_sources0和_portToV1SourceMap
if (NULL != rlm && NULL == rlm->_sources0) {
rlm->_sources0 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
rlm->_sources1 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
rlm->_portToV1SourceMap = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL);
}
//如果_sources0和_sources1中都不包含传入的source
if (NULL != rlm && !CFSetContainsValue(rlm->_sources0, rls) && !CFSetContainsValue(rlm->_sources1, rls)) {
if (0 == rls->_context.version0.version) {
//如果version是0,则加到_sources0
CFSetAddValue(rlm->_sources0, rls);
} else if (1 == rls->_context.version0.version) {
//如果version是1,则加到_sources1
CFSetAddValue(rlm->_sources1, rls);
__CFPort src_port = rls->_context.version1.getPort(rls->_context.version1.info);
if (CFPORT_NULL != src_port) {
//此处只有在加到source1的时候才会把souce和一个mach_port_t对应起来
//可以理解为,source1可以通过内核向其端口发送消息来主动唤醒runloop
CFDictionarySetValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port, rls);
__CFPortSetInsert(src_port, rlm->_portSet);
}
}
__CFRunLoopSourceLock(rls);
//把runloop加入到source的_runLoops中
if (NULL == rls->_runLoops) {
rls->_runLoops = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeBagCallBacks); // sources retain run loops!
}
CFBagAddValue(rls->_runLoops, rl);
__CFRunLoopSourceUnlock(rls);
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.schedule) {
doVer0Callout = true;
}
}
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
b、CFRunLoopRemoveSource
remove
操作和add
操作的逻辑基本一致,很容易理解。
//移除source
void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) {
__CFRunLoopLock(rl);
//如果是kCFRunLoopCommonModes,则从_commonModes的所有mode中移除该source
if (modeName == kCFRunLoopCommonModes) {
if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rls)) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
CFSetRemoveValue(rl->_commonModeItems, rls);
if (NULL != set) {
CFTypeRef context[2] = {rl, rls};
/* remove new item from all common-modes */
CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context);
CFRelease(set);
}
} else {
}
} else {
//根据modeName查找mode,如果不存在,返回NULL
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false);
if (NULL != rlm && ((NULL != rlm->_sources0 && CFSetContainsValue(rlm->_sources0, rls)) || (NULL != rlm->_sources1 && CFSetContainsValue(rlm->_sources1, rls)))) {
CFRetain(rls);
//根据source版本做对应的remove操作
if (1 == rls->_context.version0.version) {
__CFPort src_port = rls->_context.version1.getPort(rls->_context.version1.info);
if (CFPORT_NULL != src_port) {
CFDictionaryRemoveValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port);
__CFPortSetRemove(src_port, rlm->_portSet);
}
}
CFSetRemoveValue(rlm->_sources0, rls);
CFSetRemoveValue(rlm->_sources1, rls);
__CFRunLoopSourceLock(rls);
if (NULL != rls->_runLoops) {
CFBagRemoveValue(rls->_runLoops, rl);
}
__CFRunLoopSourceUnlock(rls);
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.cancel) {
doVer0Callout = true;
}
}
doRLSRelease = true;
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
4、添加Observer和Timer
添加observer
和timer
的逻辑和添加source
类似。
区别在于observer
和timer只能被添加到一个RunLoop
的一个或者多个mode
中,比如一个timer
被添加到主线程的RunLoop
中,则不能再把该timer
添加到子线程的RunLoop
,而source
没有这个限制,不管是哪个RunLoop
,只要mode
中没有,就可以添加。
这个区别在文章最开始的结构体中也可以发现,CFRunLoopSource
结构体中有保存RunLoop
对象的数组,而CFRunLoopObserver
和CFRunLoopTimer
只有单个RunLoop
对象。
五、RunLoop运行原理
1、CFRunLoopRun
在CFRunLoopRun
函数中调用了CFRunLoopRunSpecific
函数,runloop
参数传入当前RunLoop
对象,modeName
参数传入kCFRunLoopDefaultMode
。
//默认在kCFRunLoopDefaultMode下运行runloop
void CFRunLoopRun(void) {
CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
}
2、CFRunLoopRunInMode
在CFRunLoopRunInMode
函数中调用了CFRunLoopRunSpecific
函数。可以看出,虽然RunLoop
有很多个mode
,但是RunLoop
在run
的时候必须只能指定其中一个mode
,运行起来之后,被指定的mode
即为currentMode
。
//用指定的Mode启动,允许设置RunLoop超时时间
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
3、CFRunLoopRunSpecific
- 如果指定了一个不存在的
mode
来运行RunLoop
,那么会失败,mode
也不会被创建,所以这里传入的mode
必须是存在的 - 如果指定了一个
mode
,但是这个mode
中不包含任何modeItem
,那么RunLoop
也不会运行,所以必须要传入至少包含一个modeItem
的mode
- 在进入
run loop
之前通知observer
,状态为kCFRunLoopEntry
- 在退出
run loop
之后通知observer
,状态为kCFRunLoopExit
/*
* 指定mode运行runloop
* @param rl 当前运行的runloop
* @param modeName 需要运行的mode的name
* @param seconds runloop的超时时间
* @param returnAfterSourceHandled 是否处理完事件就返回
*/
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
/// 首先根据modeName找到对应mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false);
/// 如果mode里没有source/timer/observer, 直接返回。
if (__CFRunLoopModeIsEmpty(currentMode)) return;
/// 1. 通知 Observers: RunLoop 即将进入 loop。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
/// 内部函数,进入loop
__CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
Boolean sourceHandledThisLoop = NO;
int retVal = 0;
do {
/// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
/// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
/// 执行被加入的block
__CFRunLoopDoBlocks(runloop, currentMode);
/// 4. RunLoop 触发 Source0 (非port) 回调。
sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
/// 执行被加入的block
__CFRunLoopDoBlocks(runloop, currentMode);
/// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
if (__Source0DidDispatchPortLastTime) {
Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
if (hasMsg) goto handle_msg;
}
/// 6. 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
if (!sourceHandledThisLoop) {
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
}
/// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
/// 一个基于 port 的Source 的事件
/// 一个 Timer 到时间了
/// RunLoop 自身的超时时间到了
/// 被其他什么调用者手动唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
}
/// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
/// 收到消息,处理消息。
handle_msg:
/// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
if (msg_is_timer) {
__CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
}
/// 9.2 如果有dispatch到main_queue的block,执行block。
else if (msg_is_dispatch) {
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
}
/// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
else {
CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
if (sourceHandledThisLoop) {
mach_msg(reply, MACH_SEND_MSG, reply);
}
}
/// 执行加入到Loop的block
__CFRunLoopDoBlocks(runloop, currentMode);
if (sourceHandledThisLoop && stopAfterHandle) {
/// 进入loop时参数说处理完事件就返回。
retVal = kCFRunLoopRunHandledSource;
} else if (timeout) {
/// 超出传入参数标记的超时时间了
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(runloop)) {
/// 被外部调用者强制停止了
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
/// source/timer/observer一个都没有了
retVal = kCFRunLoopRunFinished;
}
/// 如果没超时,mode里没空,loop也没被停止,那继续loop。
} while (retVal == 0);
}
/// 10. 通知 Observers: RunLoop 即将退出。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
}
4、__CFRunLoopServiceMachPort
第7步调用了__CFRunLoopServiceMachPort
函数,这个函数在run loop
中起到了至关重要的作用。
/**
* 接收指定内核端口的消息
*
* @param port 接收消息的端口
* @param buffer 消息缓冲区
* @param buffer_size 消息缓冲区大小
* @param livePort 暂且理解为活动的端口,接收消息成功时候值为msg->msgh_local_port,超时时为MACH_PORT_NULL
* @param timeout 超时时间,单位是ms,如果超时,则RunLoop进入休眠状态
*
* @return 接收消息成功时返回true 其他情况返回false
*/
static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout) {
Boolean originalBuffer = true;
kern_return_t ret = KERN_SUCCESS;
for (;;) { /* In that sleep of death what nightmares may come ... */
mach_msg_header_t *msg = (mach_msg_header_t *)*buffer;
msg->msgh_bits = 0; //消息头的标志位
msg->msgh_local_port = port; //源(发出的消息)或者目标(接收的消息)
msg->msgh_remote_port = MACH_PORT_NULL; //目标(发出的消息)或者源(接收的消息)
msg->msgh_size = buffer_size; //消息缓冲区大小,单位是字节
msg->msgh_id = 0; //唯一id
if (TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } else { CFRUNLOOP_POLL(); }
//通过mach_msg发送或者接收的消息都是指针,
//如果直接发送或者接收消息体,会频繁进行内存复制,损耗性能
//所以XNU使用了单一内核的方式来解决该问题,所有内核组件都共享同一个地址空间,因此传递消息时候只需要传递消息的指针
ret = mach_msg(msg,
MACH_RCV_MSG|MACH_RCV_LARGE|((TIMEOUT_INFINITY != timeout) ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV),
0,
msg->msgh_size,
port,
timeout,
MACH_PORT_NULL);
CFRUNLOOP_WAKEUP(ret);
//接收/发送消息成功,给livePort赋值为msgh_local_port
if (MACH_MSG_SUCCESS == ret) {
*livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL;
return true;
}
//MACH_RCV_TIMEOUT
//超出timeout时间没有收到消息,返回MACH_RCV_TIMED_OUT
//此时释放缓冲区,把livePort赋值为MACH_PORT_NULL
if (MACH_RCV_TIMED_OUT == ret) {
if (!originalBuffer) free(msg);
*buffer = NULL;
*livePort = MACH_PORT_NULL;
return false;
}
//MACH_RCV_LARGE
//如果接收缓冲区太小,则将过大的消息放在队列中,并且出错返回MACH_RCV_TOO_LARGE,
//这种情况下,只返回消息头,调用者可以分配更多的内存
if (MACH_RCV_TOO_LARGE != ret) break;
//此处给buffer分配更大内存
buffer_size = round_msg(msg->msgh_size + MAX_TRAILER_SIZE);
if (originalBuffer) *buffer = NULL;
originalBuffer = false;
*buffer = realloc(*buffer, buffer_size);
}
HALT;
return false;
}
Demo
Demo在我的Github上,欢迎下载。
RunLoopDemo