“If this property is NO, scrolling is permitted in both horizontal and vertical directions. If this property is YES and the user begins dragging in one general direction (horizontally or vertically), the scroll view disables scrolling in the other direction. If the drag direction is diagonal, then scrolling will not be locked and the user can drag in any direction until the drag completes. The default value is NO”
- (void)threadMain { // The application uses garbage collection, so no autorelease pool is needed. NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop]; // Create a run loop observer and attach it to the run loop. CFRunLoopObserverContext context = {0, self, NULL, NULL, NULL}; CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context); if (observer) { CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop]; CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode); } // Create and schedule the timer. [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doFireTimer:) userInfo:nil repeats:YES]; NSInteger loopCount = 10; do { // Run the run loop 10 times to let the timer fire. [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; loopCount--; } while (loopCount); }
- (void)skeletonThreadMain { // Set up an autorelease pool here if not using garbage collection. BOOL done = NO; // Add your sources or timers to the run loop and do any other setup. do { // Start the run loop but return after each source is handled. SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES); // If a source explicitly stopped the run loop, or if there are no // sources or timers, go ahead and exit. if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) done = YES; // Check for any other exit conditions here and set the // done variable as needed. } while (!done); // Clean up code here. Be sure to release any allocated autorelease pools. }
// Client interface for registering commands to process - (void)addCommand:(NSInteger)command withData:(id)data; - (void)fireAllCommandsOnRunLoop:(CFRunLoopRef)runloop;
@end
// These are the CFRunLoopSourceRef callback functions. void RunLoopSourceScheduleRoutine (void *info, CFRunLoopRef rl, CFStringRef mode); void RunLoopSourcePerformRoutine (void *info); void RunLoopSourceCancelRoutine (void *info, CFRunLoopRef rl, CFStringRef mode);
// RunLoopContext is a container object used during registration of the input source. @interfaceRunLoopContext : NSObject { CFRunLoopRef runLoop; RunLoopSource* source; } @property (readonly) CFRunLoopRef runLoop; @property (readonly) RunLoopSource* source;
// Create and schedule the first timer. NSDate* futureDate = [NSDate dateWithTimeIntervalSinceNow:1.0]; NSTimer* myTimer = [[NSTimer alloc] initWithFireDate:futureDate interval:0.1 target:self selector:@selector(myDoFireTimer1:) userInfo:nil repeats:YES]; [myRunLoop addTimer:myTimer forMode:NSDefaultRunLoopMode];
// Create and schedule the second timer. [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(myDoFireTimer2:) userInfo:nil repeats:YES];
- (void)launchThread { NSPort* myPort = [NSMachPort port]; if (myPort) { // This class handles incoming port messages. [myPort setDelegate:self];
// Install the port as an input source on the current run loop. [[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];
// Detach the thread. Let the worker release the port. [NSThread detachNewThreadSelector:@selector(LaunchThreadWithPort:) toTarget:[MyWorkerClass class] withObject:myPort]; } }
// Let the run loop process things. do { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } while (![workerObj shouldExit]);
if (messageObj) { // Finish configuring the message and send it immediately. [messageObj setMsgId:setMsgid:kCheckinMessage]; [messageObj sendBeforeDate:[NSDate date]]; } }
// Configure the object and add it to the current run loop. [localPort setDelegate:self]; [[NSRunLoop currentRunLoop] addPort:localPort forMode:NSDefaultRunLoopMode];
// Register the port using a specific name. The name must be unique. NSString* localPortName = [NSString stringWithFormat:@"MyPortName"]; [[NSMessagePortNameServer sharedInstance] registerPort:localPort name:localPortName];
if (myPort != NULL) { // The port was successfully created. // Now create a run loop source for it. rlSource = CFMessagePortCreateRunLoopSource(NULL, myPort, 0);
if (rlSource) { // Add the source to the current run loop. CFRunLoopAddSource(CFRunLoopGetCurrent(), rlSource, kCFRunLoopDefaultMode);
// Once installed, these can be freed. CFRelease(myPort); CFRelease(rlSource); } }
// Create the thread and continue processing. MPTaskID taskID; return(MPCreateTask(&ServerThreadEntryPoint, (void*)myPortName, kThreadStackSize, NULL, NULL, NULL, 0, &taskID)); }
OSStatus ServerThreadEntryPoint(void* param) { // Create the remote port to the main thread. CFMessagePortRef mainThreadPort; CFStringRef portName = (CFStringRef)param;
// Free the string that was passed in param. CFRelease(portName);
// Create a port for the worker thread. CFStringRef myPortName = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.MyApp.Thread-%d"), MPCurrentTaskID());
// Store the port in this thread’s context info for later reference. CFMessagePortContext context = {0, mainThreadPort, NULL, NULL, NULL}; Boolean shouldFreeInfo; Boolean shouldAbort = TRUE;
if (shouldFreeInfo) { // Couldn't create a local port, so kill the thread. MPExit(0); }
CFRunLoopSourceRef rlSource = CFMessagePortCreateRunLoopSource(NULL, myPort, 0); if (!rlSource) { // Couldn't create a local port, so kill the thread. MPExit(0); }
// Add the source to the current run loop. CFRunLoopAddSource(CFRunLoopGetCurrent(), rlSource, kCFRunLoopDefaultMode);
// Once installed, these can be freed. CFRelease(myPort); CFRelease(rlSource);
// Package up the port name and send the check-in message. CFDataRef returnData = nil; CFDataRef outData; CFIndex stringLength = CFStringGetLength(myPortName); UInt8* buffer = CFAllocatorAllocate(NULL, stringLength, 0);
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,会进入消息转发阶段,如果消息三次转发流程仍未实现,则程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。
Strip Debug Symbols During Copy 和 Symbols Hidden by Default 在release版本应该设为yes,可以去除不必要的调试符号。Symbols Hidden by Default会把所有符号都定义成”private extern”,设了后会减小体积。
另外注意Xcode里面的Deployment选项,Deployment Postprocessing这个配置项如果使用xcode打包,xcode会默认把这个变量置为YES, 如果使用脚本打包,记得设置。Symbols Hidden by Default设置为YES Make Strings Read-Only 设置为YES
编译选项:LTO,即Link Time Optimization。
苹果在2016年的WWDC What’s new in LLVM中详细介绍了这一功能。LTO能带来的优化有: (1)将一些函数內联化 (2)去除了一些无用代码 (3)对程序有全局的优化作用 在build setting中开启Link-Time Optimization为Incremental,经测试可缩减安装包大小500KB左右。苹果还声称LTO对app的运行速度也有正向帮助。 但LTO也会带来一点副作用。LTO会降低编译链接的速度,因此只建议在打正式包时开启;开启了LTO之后,link map的可读性明显降低,多出了很多数字开头的“类”(LTO的全局优化导致的),导致我们还经常需要手动关闭LTO打包来阅读link map。
假如我通过代理访问 A 网站,对于 A 来说,它会把代理当做客户端,完全察觉不到真正客户端的存在,这实现了隐藏客户端 IP 的目的。当然代理也可以修改 HTTP 请求头部,通过 X-Forwarded-IP 这样的自定义头部告诉服务端真正的客户端 IP。但服务器无法验证这个自定义头部真的是由代理添加,还是客户端修改了请求头,所以从 HTTP 头部字段获取 IP 时,需要格外小心。
还有一种情况是访问 A 网站时,实际上访问的是代理,代理收到请求报文后,再向真正提供服务的服务器发起请求,并将响应转发给浏览器。这种情况一般被称之为反向代理,它可以用来隐藏服务器 IP 及端口。一般使用反向代理后,需要通过修改 DNS 让域名解析到代理服务器 IP,这时浏览器无法察觉到真正服务器的存在,当然也就不需要修改配置了。反向代理是 Web 系统最为常见的一种部署方式.
/** * 运行run loop * * @param rl 运行的RunLoop对象 * @param rlm 运行的mode * @param seconds run loop超时时间 * @param stopAfterHandle true:run loop处理完事件就退出 false:一直运行直到超时或者被手动终止 * @param previousMode 上一次运行的mode * * @return 返回4种状态 */ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { //获取系统启动后的CPU运行时间,用于控制超时时间 uint64_t startTSR = mach_absolute_time(); //如果RunLoop或者mode是stop状态,则直接return,不进入循环 if (__CFRunLoopIsStopped(rl)) { __CFRunLoopUnsetStopped(rl); return kCFRunLoopRunStopped; } elseif (rlm->_stopped) { rlm->_stopped =false; return kCFRunLoopRunStopped; } //mach端口,在内核中,消息在端口之间传递。 初始为0 mach_port_name_t dispatchPort =MACH_PORT_NULL; //判断是否为主线程 Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY&&NULL== previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY&&0== _CFGetTSD(__CFTSDKeyIsInGCDMainQ))); //如果在主线程 && runloop是主线程的runloop && 该mode是commonMode,则给mach端口赋值为主线程收发消息的端口 if (libdispatchQSafe && (CFRunLoopGetMain() == rl) &&CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF(); #ifUSE_DISPATCH_SOURCE_FOR_TIMERS mach_port_name_t modeQueuePort =MACH_PORT_NULL; if (rlm->_queue) { //mode赋值为dispatch端口_dispatch_runloop_root_queue_perform_4CF modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue); if (!modeQueuePort) { CRASH("Unable to get port for run loop mode queue (%d)", -1); } } #endif //GCD管理的定时器,用于实现runloop超时机制 dispatch_source_t timeout_timer =NULL; struct__timeout_context *timeout_context = (struct__timeout_context *)malloc(sizeof(*timeout_context)); //立即超时 if (seconds <= 0.0) { // instant timeout seconds =0.0; timeout_context->termTSR = 0ULL; } //seconds为超时时间,超时时执行__CFRunLoopTimeout函数 elseif (seconds <=TIMER_INTERVAL_LIMIT) { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT); timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_retain(timeout_timer); timeout_context->ds = timeout_timer; timeout_context->rl = (CFRunLoopRef)CFRetain(rl); timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds); dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout); dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel); uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL); dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL); dispatch_resume(timeout_timer); } //永不超时 else { // infinite timeout seconds =9999999999.0; timeout_context->termTSR =UINT64_MAX; } //标志位默认为true Boolean didDispatchPortLastTime =true; //记录最后runloop状态,用于return int32_t retVal =0; do { //初始化一个存放内核消息的缓冲池 uint8_t msg_buffer[3*1024]; #ifDEPLOYMENT_TARGET_MACOSX||DEPLOYMENT_TARGET_EMBEDDED||DEPLOYMENT_TARGET_EMBEDDED_MINI mach_msg_header_t *msg =NULL; mach_port_t livePort =MACH_PORT_NULL; #elif DEPLOYMENT_TARGET_WINDOWS HANDLE livePort =NULL; Boolean windowsMessageReceived =false; #endif //取所有需要监听的port __CFPortSet waitSet = rlm->_portSet; //设置RunLoop为可以被唤醒状态 __CFRunLoopUnsetIgnoreWakeUps(rl); //2.通知observer,即将触发timer回调,处理timer事件 if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); //3.通知observer,即将触发Source0回调 if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources); //执行加入当前runloop的block __CFRunLoopDoBlocks(rl, rlm); //4.处理source0事件 //有事件处理返回true,没有事件返回false Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); if (sourceHandledThisLoop) { //执行加入当前runloop的block __CFRunLoopDoBlocks(rl, rlm); } //如果没有Sources0事件处理 并且 没有超时,poll为false //如果有Sources0事件处理 或者 超时,poll都为true Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR); //第一次do..whil循环不会走该分支,因为didDispatchPortLastTime初始化是true if (MACH_PORT_NULL!= dispatchPort &&!didDispatchPortLastTime) { #ifDEPLOYMENT_TARGET_MACOSX||DEPLOYMENT_TARGET_EMBEDDED||DEPLOYMENT_TARGET_EMBEDDED_MINI //从缓冲区读取消息 msg = (mach_msg_header_t *)msg_buffer; //5.接收dispatchPort端口的消息,(接收source1事件) if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) { //如果接收到了消息的话,前往第9步开始处理msg goto handle_msg; } #elif DEPLOYMENT_TARGET_WINDOWS if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) { goto handle_msg; } #endif } didDispatchPortLastTime =false; //6.通知观察者RunLoop即将进入休眠 if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); //设置RunLoop为休眠状态 __CFRunLoopSetSleeping(rl); // do not do any user callouts after this point (after notifying of sleeping) // Must push the local-to-this-activation ports in on every loop // iteration, as this mode could be run re-entrantly and we don't // want these ports to get serviced. __CFPortSetInsert(dispatchPort, waitSet); __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); #ifDEPLOYMENT_TARGET_MACOSX||DEPLOYMENT_TARGET_EMBEDDED||DEPLOYMENT_TARGET_EMBEDDED_MINI #ifUSE_DISPATCH_SOURCE_FOR_TIMERS //这里有个内循环,用于接收等待端口的消息 //进入此循环后,线程进入休眠,直到收到新消息才跳出该循环,继续执行run loop do { if (kCFUseCollectableAllocator) { objc_clear_stack(0); memset(msg_buffer, 0, sizeof(msg_buffer)); } msg = (mach_msg_header_t *)msg_buffer; //7.接收waitSet端口的消息 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ?0 : TIMEOUT_INFINITY); //收到消息之后,livePort的值为msg->msgh_local_port, if (modeQueuePort !=MACH_PORT_NULL&& livePort == modeQueuePort) { // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer. while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue)); if (rlm->_timerFired) { // Leave livePort as the queue port, and service timers below rlm->_timerFired =false; break; } else { if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); } } else { // Go ahead and leave the inner loop. break; } } while (1); #else if (kCFUseCollectableAllocator) { objc_clear_stack(0); memset(msg_buffer, 0, sizeof(msg_buffer)); } msg = (mach_msg_header_t *)msg_buffer; __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ?0 : TIMEOUT_INFINITY); #endif #elif DEPLOYMENT_TARGET_WINDOWS // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages. __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ?0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived); #endif __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); // Must remove the local-to-this-activation ports in on every loop // iteration, as this mode could be run re-entrantly and we don't // want these ports to get serviced. Also, we don't want them left // in there if this function returns. __CFPortSetRemove(dispatchPort, waitSet); __CFRunLoopSetIgnoreWakeUps(rl); // user callouts now OK again //取消runloop的休眠状态 __CFRunLoopUnsetSleeping(rl); //8.通知观察者runloop被唤醒 if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); //9.处理收到的消息 handle_msg:; __CFRunLoopSetIgnoreWakeUps(rl); #ifDEPLOYMENT_TARGET_WINDOWS if (windowsMessageReceived) { // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); if (rlm->_msgPump) { rlm->_msgPump(); } else { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE|PM_NOYIELD)) { TranslateMessage(&msg); DispatchMessage(&msg); } } __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); sourceHandledThisLoop =true; // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later. // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling. __CFRunLoopSetSleeping(rl); __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL); __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); __CFRunLoopUnsetSleeping(rl); // If we have a new live port then it will be handled below as normal } #endif if (MACH_PORT_NULL== livePort) { CFRUNLOOP_WAKEUP_FOR_NOTHING(); // handle nothing //通过CFRunloopWake唤醒 } elseif (livePort == rl->_wakeUpPort) { CFRUNLOOP_WAKEUP_FOR_WAKEUP(); //什么都不干,跳回2重新循环 // do nothing on Mac OS #ifDEPLOYMENT_TARGET_WINDOWS // Always reset the wake up port, or risk spinning forever ResetEvent(rl->_wakeUpPort); #endif } #ifUSE_DISPATCH_SOURCE_FOR_TIMERS //如果是定时器事件 elseif (modeQueuePort !=MACH_PORT_NULL&& livePort == modeQueuePort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); //9.1 处理timer事件 if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { // Re-arm the next timer, because we apparently fired early __CFArmNextTimerInMode(rlm, rl); } } #endif #ifUSE_MK_TIMER_TOO //如果是定时器事件 elseif (rlm->_timerPort !=MACH_PORT_NULL&& livePort == rlm->_timerPort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled. // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754 //9.1处理timer事件 if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { // Re-arm the next timer __CFArmNextTimerInMode(rlm, rl); } } #endif //如果是dispatch到main queue的block elseif (livePort == dispatchPort) { CFRUNLOOP_WAKEUP_FOR_DISPATCH(); __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL); #ifDEPLOYMENT_TARGET_WINDOWS void *msg =0; #endif //9.2执行block __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL); __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); sourceHandledThisLoop =true; didDispatchPortLastTime =true; } else { CFRUNLOOP_WAKEUP_FOR_SOURCE(); // Despite the name, this works for windows handles as well CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort); // 有source1事件待处理 if (rls) { #ifDEPLOYMENT_TARGET_MACOSX||DEPLOYMENT_TARGET_EMBEDDED||DEPLOYMENT_TARGET_EMBEDDED_MINI mach_msg_header_t *reply =NULL; //9.2 处理source1事件 sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop; if (NULL!= reply) { (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply); } #elif DEPLOYMENT_TARGET_WINDOWS sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop; #endif } } #ifDEPLOYMENT_TARGET_MACOSX||DEPLOYMENT_TARGET_EMBEDDED||DEPLOYMENT_TARGET_EMBEDDED_MINI if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); #endif __CFRunLoopDoBlocks(rl, rlm); if (sourceHandledThisLoop && stopAfterHandle) { //进入run loop时传入的参数,处理完事件就返回 retVal = kCFRunLoopRunHandledSource; }elseif (timeout_context->termTSR < mach_absolute_time()) { //run loop超时 retVal = kCFRunLoopRunTimedOut; }elseif (__CFRunLoopIsStopped(rl)) { //run loop被手动终止 __CFRunLoopUnsetStopped(rl); retVal = kCFRunLoopRunStopped; }elseif (rlm->_stopped) { //mode被终止 rlm->_stopped =false; retVal = kCFRunLoopRunStopped; }elseif (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { //mode中没有要处理的事件 retVal = kCFRunLoopRunFinished; } //除了上面这几种情况,都继续循环 } while (0== retVal); if (timeout_timer) { dispatch_source_cancel(timeout_timer); dispatch_release(timeout_timer); } else { free(timeout_context); } return retVal; }