您的位置:1010cc时时彩经典版 > 1010cc时时彩经典版 > 1010cc时时彩经典版Runloop运行状态分析,的内部逻

1010cc时时彩经典版Runloop运行状态分析,的内部逻

发布时间:2019-09-12 12:10编辑:1010cc时时彩经典版浏览(148)

    跟半数以上开拓者同样,小编也一度吸引于runloop,最先只询问能够经过runloop一些监听事件的布告来做一些业务,优化品质。关于runloop源码的基础知识,本文不做论述,能够参见众神的文章:

    //RunLoop的实现

     

    1、先来一张图看下runloop 的运营流程:

    1010cc时时彩经典版 1

    Runloop 运行状态图深入分析

    依附苹果在文书档案里的辨证,RunLoop 内部的逻辑大致如下:

    ibireme:《浓厚驾驭RunLoop》sunyawang:《RunLoop连串之源码解析》xiaoxiaobukuang:《RunLoop》

    intCFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {

    上学iOS开辟一般都以从UI先导的,从只了然从IB拖控件,到精晓怎么在章程里写代码,然后会呈现怎么的视图,产生什么的轩然大波,等等。其实程序从起步上马,一贯都是依照苹果封装好的代码运营着,暴光的一些品质和办法作为接口,是让大家在加以的不二等秘书籍里写代码完毕自定义功用,做出五光十色的应用。那么些措施的调用顺序最为根本,熟谙了前后相继运维和形式调用的相继,技术够越来越好地操控程序和代码,尽量幸免Xcode不报错又完结持续成效的BUG。从Xcode的线程函数调用栈能够见到有的主意调用顺序。

    2、具体描述:RunLoop内部的骨干流程

    (1)通告观望者RunLoop运转
    (2)文告观看者将在管理Timer
    (3)布告观望者将要管理Source0
    (4)触发Source0回调
    (5)假若有Source1(基于port)处于ready状态,直接管理该Source1然后跳转到 第(9)步去管理音讯
    (6)若无待管理新闻,则公告阅览者RunLoop所在线程就要步入休眠。
    (7)休眠前,RunLoop会增加二个dispatchPort,底层调用mach_ msg接收mach_ port的音讯。线程踏入休眠,直到上面有个别事件触发唤醒线程:

    • 据他们说port的Source1事件达到
    • 提姆er时间到达
    • RunLoop运转时设置的最大超时时间到了
    • 手动唤醒

    (8)唤醒后,将休眠前增加的dispatchPort移除,并通报观望者RunLoop已经被唤起
    (9)通过handle_ msg处理音信
    (10)假使新闻是Timer类型,则触发该提姆er的回调
    (11)要是新闻是dispatch到main_ queue的block,执行block
    (12)假使音讯是Source1类型,则管理Source1回调
    (13)以下条件中级知识分子足时候退出循环,不然从(2)继续循环

    • 事件管理达成何况运转RunLoop的时候参数设置为一次性施行
    • 开发银行RunLoop时设置的最大运维时刻到期
    • RunLoop被外表调用强行终止
    • 启动RunLoop的mode items为空

    (14)上一步退出循环后退出RunLoop,布告观察者RunLoop退出

    1010cc时时彩经典版 2

    • 提议普及传播runloop作品中指鹿为马
    • 透过代码论证错误
    • 通过demo论证错误

    // 0.1依据modeName找到相应mode


    3、入眼强调:

    Runloop被升迁后,一定是:先拍卖唤醒它的风云,管理完后再自笔者商量是或不是满意退出的标准化,满意则退出Runloop,不满意才再次回到步骤(2)去巡回
    能够断点验证:唤醒后先拍卖唤醒runloop的时日,之后才会走到步骤2的下结论。

    其内部代码整理如下 (太长了不想看能够一向跳过去,后边会有证实):

    runloop解读小说中的错误

    自家也瞧着众神的稿子才对runloop有了相比较深远驾驭,前段时间和煦到底利用零散的时光把runloop源码也看了一回,才意识众四个人都误会了runloop!!就拿上面这张大多稿子中都说起的图样和流程来讲:

    1010cc时时彩经典版 3摘自《深远掌握RunLoop》

    那是runloop运维流程图,但实际那一个图里面有五个错误,请看下边标记图:

    1010cc时时彩经典版 4错误标记图

    • 率先个谬误 “source0” 应该是小编笔误,图中错误将source1 写成source0;

    • 第二个错误 "5. 只要有source1,跳到第9步" 从图和作者的代码注释中都能看出是领略有不当,这里也便是本文入眼描述的开始和结果

    先说结论,再逐月验证:

    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName,false);

     

    4、Runloop 的源

    • Source0 :只富含多少个函数指针(回调方法),不可能自动触发,只好手动触发,触发方式是先通过CFRunLoopSourceSignal(source)将以此Source标志为待管理,然后再调用CFRunLoopWakeUp(runloop) 来唤醒RunLoop管理那一个事件。

    • Source1 :基于port的Source源,包蕴二个port和二个函数指针(回调方法)。该Source源可由此基础和另外线程相互发送音信,何况能够积极唤醒RunLoop。

    /// 用DefaultMode启动

    此地实在剖断的是 主线程是不是有必要管理的平地风波,若无则调到第9步,这里跟source1未有涉及!

    // 0.2举个例子mode里未有source/timer直接回到。

    --零--从程序运行上马到view显示:

    start---->(加载framework,动态静态链接库,运转图片,Info.plist,pch等)---->main函数---->UIApplicationMain函数:

      - 初始化UIApplication单例对象
      - 初始化AppDelegate对象,并设为UIApplication对象的代理
      - 检查Info.plist设置的xib文件是否有效,如果有则解冻Nib文件并设置outlets,创建显示key window、rootViewController、与rootViewController关联的根view(没有关联则看rootViewController同名的xib),否则launch之后由程序员手动加载。
      - 建立一个主事件循环,其中包含UIApplication的Runloop来开始处理事件。
    

    style="font-size: 14px; background-color: #ff00ff;">UIApplication
      1、通过window管理视图;
    style="font-size: 14px;">  2、发送Runloop封装好的control音信给target;
    style="font-size: 14px;">  3、管理U奥德赛L,应用图标警告,联网状态,状态栏,远程事件等。
    style="font-size: 14px; background-color: #ff00ff;">AppDelegate
    style="font-size: 14px;">管理UIApplication生命周期和应用的五种状态(notRunning/inactive/active/background/suspend)。
    Key Window
      1、显示view;
    style="font-size: 14px;">  2、管理rootViewcontroller生命周期;
    style="font-size: 14px;">  3、发送UIApplication传来的事件消息给view。
    style="font-size: 14px; background-color: #ff00ff;">rootViewController
    style="font-size: 14px;">1、管理view(view生命周期;view的数据源/代理;view与superView之间事件响应nextResponder的“备胎”);
    2、分界面跳转与传值;
    3、状态栏,显示器旋转。
    style="font-size: 14px; background-color: #ff00ff;">view
    style="font-size: 14px;">  1、通过作为CALayer的代理,管理layer的渲染(顺序差不离是先更新约束,再layout再display)和卡通(暗许layer的天性可动画,view暗中认可禁止,在UIView的block分类方法里才打开动画)。layer是瑞虎GBA纹理,通过和mask位图(含阿尔法属性)关联将合成后的layer纹理填充在像素点内,GPU每1/60秒将计算出的纹理display在像素点中。
    style="font-size: 14px;">  2、布局子控件(显示屏旋转或许子视图布局变动时,view会重新布局)。
    style="font-size: 14px;">  3、事件响应:event和guesture。
    插播调控器生命周期
    style="background-color: #ff00ff;">runloop:
    style="font-size: 14px;">  1、(要让马儿跑)通过do-while死循环让程序持续运转:接收顾客输入,调节处管事人件时间。
    style="font-size: 14px;">  2、(要让马儿少吃草)通过mach_msg()让runloop没事时步入trap状态,节省CPU资源。


      关于程序运营原理以及各样控件的素材,已经有太多材质介绍,平日大家也时有时接触平时使用,但有关Runloop的质感,官方文档总是太过粗略,网络能源说法也不太统一,只好从CFRunLoopRef开源代码初始,试着学习总结下。(NSRunloop是对CFRunloopRef的面向对象封装,然则不是线程安全)。


    问题:

    UI刷新是分开为Source1事件,而事件响应属于source0。小编一时那样敞亮:因为UI刷新是主动开展的,无需外界手动触发。而事件响应(比如:touch事件的接触)等必要外表手动触发。鉴于是还是不是足以手动触发以及来自新闻来根本等知识点,故将UI刷新划分为Source1事件,而事件响应划分为source0。
    假设明白有误,或许有更具说服力的驾驭,请马上联系小编。

    void CFRunLoopRun(void) {

    据此理应改成“5. 一旦当前是主线程的runloop,何况主线程有事儿,跳到第9步”

    大家直接上源码(版本CF-1151.16)分析一下,直接看那句话对应的代码:

    if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime){ msg = (mach_msg_header_t *)msg_buffer; if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) { goto handle_msg; }}
    

    能够看看跳转到第9步(goto handle_msg)的逻辑是决断__CFRunLoop瑟维斯MachPort函数的重临值是或不是为真,而这些if对应的就是上文描述“要是有source1”,那么那句话是这几个意思吧? 发轫小编也是如此感觉的,直到笔者看看了背后下一段第7步“休眠”的代码:

    // 第七步,进入循环开始不断的读取端口信息,如果端口有唤醒信息则唤醒当前runLoop__CFPortSet waitSet = rlm->_portSet;......if (kCFUseCollectableAllocator) { memset(msg_buffer, 0, sizeof(msg_buffer));}// waitSet 为所有需要监听的port集合, TIMEOUT_INFINITY表示一直等待msg = (mach_msg_header_t *)msg_buffer;__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
    

    这里面出现了地点的一律的__CFRunLoopServiceMachPort方法, 单拎出来比对下,

    __CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy)

    相比较后发觉,参数中首先个参数和尾数第八个参数不一样。大家透过__CFRunLoopServiceMachPort的源码来剖析下,个中首要关切:

    • livePort的赋值用于函数外界使用;
    • __CFRunLoopServiceMachPort方法中mach_msg的参数MACH_RCV_MSG代表在收受消息;
    • __CFRunLoop瑟维斯MachPort参数timeout对于双方入参分别是0和TIMEOUT_INFINITY,分别代表查询到当时回到和直接等候有新闻再再次回到;
    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, voucher_mach_msg_state_t *voucherState, voucher_t *voucherCopy) { 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; if (TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } else { CFRUNLOOP_POLL(); } ret = mach_msg(msg, MACH_RCV_MSG|(voucherState ? MACH_RCV_VOUCHER : 0)|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); // Take care of all voucher-related work right after mach_msg. // If we don't release the previous voucher we're going to leak it. voucher_mach_msg_revert(*voucherState); // Someone will be responsible for calling voucher_mach_msg_revert. This call makes the received voucher the current one. *voucherState = voucher_mach_msg_adopt; if (voucherCopy) { if (*voucherState != VOUCHER_MACH_MSG_STATE_UNCHANGED) { *voucherCopy = voucher_copy(); } else { *voucherCopy = NULL; } } CFRUNLOOP_WAKEUP; if (MACH_MSG_SUCCESS == ret) { *livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL; return true; } if (MACH_RCV_TIMED_OUT == ret) { if (!originalBuffer) free; *buffer = NULL; *livePort = MACH_PORT_NULL; return false; } if (MACH_RCV_TOO_LARGE != ret) break; buffer_size = round_msg(msg->msgh_size   MAX_TRAILER_SIZE); if (originalBuffer) *buffer = NULL; originalBuffer = false; *buffer = realloc(*buffer, buffer_size); } HALT; return false;}
    

    从代码中大家能够大概看看,休眠时调用那几个措施的作用正是监听剖断waitSet中享有port,假如那么些port中有多少个面世音信,就提醒了跳出休眠,何况将唤起的port赋值给livePort。对于地点的mach_msg,我们在程序运维时打断点一定平日境遇,如下图,当runloop处于休眠时,正是底下的景色,也正是地点代码中mach_msg的timeout入参为TIMEOUT_INFINITY时阻塞式等待的意况:

    1010cc时时彩经典版 5堵塞等待新闻仓库

    上边的代码也作证了livePort用来判别是哪一种激情将休眠唤醒,通过livePort来判定是拓宽哪一类管理:

    if (MACH_PORT_NULL == livePort){ CFRUNLOOP_WAKEUP_FOR_NOTHING();}else if (livePort == rl->_wakeUpPort){ CFRUNLOOP_WAKEUP_FOR_WAKEUP();}else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort){ // 处理timer}else if (livePort == dispatchPort) { ...... // 处理主线程队列中事件 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__; ......}else { ...... // 处理Source1 sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop; ......}
    

    透过上边对__CFRunLoopServiceMachPort的源码剖判:我们着力分明了,第5步对应的代码

    if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) { goto handle_msg;}
    

    其实__CFRunLoopServiceMachPort在等的是dispatchPort这一个端口的消息,而以此端口是怎样吧? 大家本着源码向前找:

    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)));if (libdispatchQSafe && (CFRunLoopGetMain && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();
    

    笔者们第一看if判别中的 (CFRunLoopGetMain,当中rl表示目前的runloop,查看CFRunLoopGetMain()源码可见再次来到的是主线程的runloop,所以这边判别正是当下runloop是还是不是是主线程的runloop,那时我们再回去上面跳转到handle_msg这段代码:

    if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) { msg = (mach_msg_header_t *)msg_buffer; if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) { goto handle_msg; }}
    

    咱俩能够看来判断是不是跳转从前先推断dispatchPort有没有音信,而再前边的准绳必得餍足MACH_PORT_NULL != dispatchPort,也正是日前必需对dispatchPort有所赋值,才会开展上面包车型客车剖断和跳转逻辑。所以那边能够小总计一下最主要的下结论:

    • 独有当前运作的runloop是主线程的runloop时,才会对dispatchPort赋值;
    • 假定dispatchPort未有赋值,则不会开展是或不是“goto handle_msg”的逻辑剖断;
    • dispatchPort赋予的值是主线程队列对应的port;
    • 若果当前运营的runloop不是主线程的runloop,那么原图中的第5步就不会设有,也正是多子线程图中空中楼阁第5步;

    综上,终于赶到大家理论的下结论:原图中第5步的相应由"5. 一旦有source1,调到第9步"改成“5. 只要当前是主线程的runloop,并且主线程有事儿,跳到第9步”。 所以最后完全流程应该是:

     1. 通知observer run loop被触发 2. 如果有timers事件的话,通知observer 3. 如果有source0要处理的话,通知observer 4. 触发所有的准备完毕的source0 5. 如果当前是主线程的runloop,并且主线程有事儿,跳到第9步 6. 通知Observer runloop将进入sleep状态 7. mach进入sleep和监听状态 8. 通知observer,runloop被woke up 9. 如果runloop是被唤醒,CFRUNLOOP_WAKEUP_FOR_WAKEUP 10. 如果用户定义的timer被触发,处理event并重启RunLoop 11. 如果dispatchPort,处理主线程 12. 如果一个source1被触发,__CFRunLoopDoSource1 13. 继续循环或通知observer runloop将要exited。
    

    末段我们再用demo来佐证一下,demo中笔者会首先则监听主线程的runloop,然后再在子线程监听子线程的runloop,打字与印刷监听的事件。先看下demo中的首要代码:

    // 添加主线程runloop监听者[self addMainObserver];// 添加子线程runloop监听者[self addOtherObserver];// 此处使用sleep是为了避免使用timer造成runloop的timer事件的干扰。sleep;dispatch_async(dispatch_get_main_queue(), ^{ CGFloat randomAlpha = (arc4random*0.01; [self.view setBackgroundColor:[UIColor colorWithWhite:0.5 alpha:randomAlpha]];});......// 添加子线程runloop监听者- addOtherObserver{ [NSThread detachNewThreadWithBlock:^{ _timer = [NSTimer scheduledTimerWithTimeInterval:3 repeats:NO block:^(NSTimer * _Nonnull timer) { NSLog(@"###cmm子线程###timer时间到"); }]; CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { switch  { case kCFRunLoopEntry: NSLog(@"###cmm子线程###进入kCFRunLoopEntry"); break; case kCFRunLoopBeforeTimers: NSLog(@"###cmm子线程###即将处理Timer事件"); break; case kCFRunLoopBeforeSources: NSLog(@"###cmm子线程###即将处理Source事件"); break; case kCFRunLoopBeforeWaiting: NSLog(@"###cmm子线程###即将休眠"); break; case kCFRunLoopAfterWaiting: NSLog(@"###cmm子线程###被唤醒"); break; case kCFRunLoopExit: NSLog(@"###cmm子线程###退出RunLoop"); break; default: break; } }); CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; CFRunLoopRun(); }];}// 添加主线程runloop监听者- addMainObserver{ CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { switch  { case kCFRunLoopEntry: NSLog(@"###cmm###进入kCFRunLoopEntry"); break; case kCFRunLoopBeforeTimers: NSLog(@"###cmm###即将处理Timer事件"); break; case kCFRunLoopBeforeSources: NSLog(@"###cmm###即将处理Source事件"); break; case kCFRunLoopBeforeWaiting: NSLog(@"###cmm###即将休眠"); break; case kCFRunLoopAfterWaiting: NSLog(@"###cmm###被唤醒"); break; case kCFRunLoopExit: NSLog(@"###cmm###退出RunLoop"); break; default: break; } }); CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); _timer1 = [NSTimer scheduledTimerWithTimeInterval:3 repeats:NO block:^(NSTimer * _Nonnull timer) { NSLog(@"###cmm###timer时间到"); }];}
    

    结缘刚刚整理的runloop的欧洲经济共同体流程解析一下预期的打字与印刷结果应该是:

    • 主线程中,假使有事情要求管理, “就要管理timer事件”-->"就要管理source事件"-->下三个巡回的"将要管理timer事件"-->"就要管理source事件",这里未有通过“将要休眠”,正是因为主线程有事儿,走入“goto handle_msg”,直接跳过休眠阶段。
    • 子线程在主线程runloop处总管务的时候,并从未打字与印刷结果生成,表明并未接触那一个goto条件。

    demo跑起来~~~大家在主线程的代码中打断点,查看旅馆和日志如下图:

    1010cc时时彩经典版 6货仓和日志

    可以开掘,如笔者辈所料:主线程的runloop在将在处理source事件后,直接跳到了 “__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__” ,也便是跳过了休眠,直接到了handle_msg对应的 else if (livePort == dispatchPort) 分支。别的我们得以在日记中窥见此时子线程的runloop已经起步,并处于休眠状态。然后大家注意下下图:

    1010cc时时彩经典版 7日志

    如图中箭头处,在大家前后相继跳过断点继续实践后,并未子线程的有关打字与印刷,表达此时子线程的runloop并不会管主线程那有些代码。

    if(__CFRunLoopModeIsEmpty(currentMode))return;

     

    消除:小编通过查看core animation相关文书档案和素材,找到以下合理依据:

    CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10,false);

    // 1.1通告Observers: RunLoop将要进入loop。---(OB会创立释放池)

    --一--Runloop

    RunLoop义务分发

    1010cc时时彩经典版 8

    iOS 展现系统原理图

    iOS 的显得系统是由 VSync 功率信号驱动的,VSync 信号由硬件石英钟生成,每分钟发(英文名:zhōng fā)出 60 次(这么些值取决设备硬件,比方 BlackBerry真机上经常是 59.97)。iOS 图形服务接受到 VSync 时限信号后,会经过 IPC 文告到 App 内。App 的 Runloop 在起步后会注册对应的 CFRunLoopSource 通过 mach_port 接收传过来的石英钟实信号布告,随后 Source 的回调会使得整个 App 的动画片与体现。

    Core Animation 在 RunLoop 中注册了三个 Observer,监听了 BeforeWaiting 和 Exit 事件。那几个 Observer 的预先级是 三千000,低于常见的别样 Observer。当一个触摸事件到来时,RunLoop 被提醒,App 中的代码会实行一些操作,比方成立和调治视图层级、设置 UIView 的 frame、修改 CALayer 的反射率、为视图增多叁个卡通;那个操作最后都会被 CALayer 捕获,并通过 CATransaction 提交到多其中间状态去(CATransaction 的文书档案略有提到这一个剧情,但并不完全)。当上边装有操作结束后,RunLoop 将在步入休眠(只怕退出)时,关怀该事件的 Observer 都会赢得公告。那时 CA 注册的不行 Observer 就能够在回调中,把具有的中间状态合併提交到 GPU 去显得;要是此处有动画,CA 会通过 DisplayLink 等体制数次触及相关流程。

    }

    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);

     

    1、与线程和自动释放池相关:
    
    2、CFRunLoopRef构造:数据结构;创建与退出;mode切换和item依赖;Runloop启动
             - CFRunLoopModeRef:数据结构(与CFRunLoopRef放一起了);创建;类型;
               modeItems:- CFRunLoopSourceRef:数据结构(source0/source1);
                                 - source0 :
                                 - source1 :
                          - CFRunLoopTimerRef:数据结构;创建与生效;相关类型(GCD的timer与CADisplayLink)
                          - CFRunLoopObserverRef:数据结构;创建与添加;监听的状态;
    3、Runloop内部逻辑:关键在两个判断点(是否睡觉,是否退出)
             - 代码实现:
             - 函数作用栈显示:
    
    4、Runloop本质:mach port和mach_msg()。
    
    5、如何处理事件:
             - 界面刷新:
             - 手势识别:
             - GCD任务:
             - timer:(与CADisplayLink)
             - 网络请求:
    
    6、应用:
             - 滑动与图片刷新;
             - 常驻子线程,保持子线程一直处理事件
    

     

     

    5、参阅

    Runloop机制探究
    iOS保持分界面流畅技能

    /// 用钦定的Mode运维,允许设置RunLoop超时时间

    // 1.2之中等高校函授数,步入loop

    Runloop

    1、与线程和电动释放池相关:

    Runloop的寄生于线程:三个线程只可以有独一对应的runloop;但那一个根runloop里可以嵌套子runloops;
    活动释放池寄生于Runloop:程序运转后,主线程注册了八个Observer监听runloop的出入与睡眠。叁个参天优先级OB监测Entry状态;一个最低优先级OB监听BeforeWaiting状态和Exit状态。
    线程(创造)-->runloop将步入-->最高优先级OB成立释放池-->runloop将睡-->最低优先级OB销毁旧池成立新池-->runloop将脱离-->最低优先级OB销毁新池-->线程(销毁)


    2、CFRunLoopRef构造:

    数据结构:

    // runloop数据结构
    struct __CFRunLoopMode {
        CFStringRef _name;            // Mode名字, 
        CFMutableSetRef _sources0;    // Set<CFRunLoopSourceRef>
        CFMutableSetRef _sources1;    // Set<CFRunLoopSourceRef>
        CFMutableArrayRef _observers; // Array<CFRunLoopObserverRef>
        CFMutableArrayRef _timers;    // Array<CFRunLoopTimerRef>
        ...
    };
    // mode数据结构
    struct __CFRunLoop {
        CFMutableSetRef _commonModes;     // Set<CFStringRef>
        CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
        CFRunLoopModeRef _currentMode;    // Current Runloop Mode
        CFMutableSetRef _modes;           // Set<CFRunLoopModeRef>
        ...
    };
    

    创制与退出:mode切换和item重视

    a 主线程的runloop自动创建,子线程的runloop默认不创建(在子线程中调用NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    获取RunLoop对象的时候,就会创建RunLoop);
    


    b runloop退出的规格:app退出;线程关闭;设置最大日子到期;modeItem为空;
    c 同一时间一个runloop只好在一个mode,切换mode只可以退出runloop,再重进内定mode(隔开分离modeItems使之互不滋扰);
    d 一个item能够加到分化mode;多个mode被标识到commonModes里(那样runloop不用切换mode)。

    启动Runloop:

    // 用DefaultMode启动
    void CFRunLoopRun(void) {
        CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
    }
    


    // 用钦赐的Mode运营,允许设置RunLoop最大日子(假Infiniti循环),推行完成是还是不是退出 int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) { return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled); }

    • CFRunLoopModeRef:
      数据结构(见上);

       style="font-size: 14px;">创建添加:runloop自动创建对应的mode;mode只能添加不能删除
      
      // 添加mode
      CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);
      

    类型:

    1. kCFRunLoopDefaultMode: 默认 mode,通常主线程在这个 Mode 下运行。
    
    2. UITrackingRunLoopMode: 追踪mode,保证Scrollview滑动顺畅不受其他 mode 影响。
    
    3. UIInitializationRunLoopMode: 启动程序后的过渡mode,启动完成后就不再使用。
    
    4: GSEventReceiveRunLoopMode: Graphic相关事件的mode,通常用不到。
    
    5: kCFRunLoopCommonModes: 占位mode,作为标记DefaultMode和CommonMode用。
    
    • modeItems:
    // 添加移除item的函数(参数:添加/移除哪个item到哪个runloop的哪个mode下)
    CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
    


    CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
    CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
    CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
    CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
    CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

    A-- CFRunLoopSourceRef:事件源于

    按照官方文档CFRunLoopSourceRef为3类,但数据结构只有两类(???)
    Port-Based Sources:与内核端口相关
    Custom Input Sources:与自定义source相关
    Cocoa Perform Selector Sources:与PerformSEL方法相关)
    

    数据结构(source0/source1);

    // source0 (manual): order(优先级),callout(回调函数)
    CFRunLoopSource {order =..., {callout =... }}
    


    // source1 (mach port):order(优先级),port:(端口), callout(回调函数) CFRunLoopSource {order = ..., {port = ..., callout =...}

    source0:event事件,只含有回调,要求标志待管理(signal),然后手动将runloop唤醒(wakeup);
    source1 :包涵三个mach_port 和三个回调,被用来通过基础和另外线程发送的新闻,能积极唤醒runloop。

    B-- CFRunLoopTimerRef:系统内“定时时钟”

    NSTimer和performSEL方法其实是对CFRunloopTimerRef的包装;runloop运转时设置的最大超时时间莫过于是GCD的dispatch_source_t类型。

    数据结构:

    // Timer:interval:(闹钟间隔), tolerance:(延期时间容忍度),callout(回调函数)
    CFRunLoopTimer {firing =..., interval = ...,tolerance = ...,next fire date = ...,callout = ...}
    

    创建与生效;

    //NSTimer:
      // 创建一个定时器(需要手动加到runloop的mode中)
       (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
    


    // 默许已经增添到主线程的runLoop的DefaultMode中

       (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
    



    // performSEL方法 // 内部会创制三个Timer到当前线程的runloop中(要是当前线程没runloop则方法行不通;performSelector:onThread: 方法放到钦点线程runloop中)

    - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
    

    连带项目(GCD的timer与CADisplayLink)

    GCD的timer:
    dispatch_source_t 类型,可以正确的参数,不用以来runloop和mode,质量消耗更加小。

    dispatch_source_set_timer(dispatch_source_t source, // 定时器对象
                                  dispatch_time_t start, // 定时器开始执行的时间
                                  uint64_t interval, // 定时器的间隔时间
                                  uint64_t leeway // 定时器的精度
                                  );
    

    CADisplayLink :
    Timer的tolerance表示最大延期时间,假如因为不通遗失了那些小时精度,这一个时间点的回调也会跳过去,不会延后施行。
    CADisplayLink 是一个和显示屏刷新率一致的机械漏刻,假设在三遍显示器刷新之间实行了贰个长任务,这里边就能够有一帧被跳过去(和 NSTimer 相似,只是未有tolerance容忍时间),产生分界面卡顿的痛感。

    C--CFRunLoopObserverRef:监听runloop状态,接收回调音讯(常见于机关释放池创制造和出卖毁)

    数据结构:

    // Observer:order(优先级),ativity(监听状态),callout(回调函数)
    CFRunLoopObserver {order = ..., activities = ..., callout = ...}
    

    始建与拉长;

    // 第一个参数用于分配该observer对象的内存空间
    // 第二个参数用以设置该observer监听什么状态
    // 第三个参数用于标识该observer是在第一次进入run loop时执行还是每次进入run loop处理时均执行
    // 第四个参数用于设置该observer的优先级,一般为0
    // 第五个参数用于设置该observer的回调函数
    // 第六个参数observer的运行状态   
    CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
          // 执行代码
    }
    

    监听的情景;

    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
        kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
        kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
        kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
        kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
        kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
    };
    

    style="font-size: 14px;">3、Runloop内部逻辑:关键在三个决断点(是还是不是睡觉,是或不是退出)

    • 代码实现:
    // RunLoop的实现
    int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {
    


    // 0.1 依据modeName找到呼应mode CFRunLoopModeRef currentMode = CFRunLoopFindMode(runloop, modeName, false); // 0.2 假诺mode里未有source/timer/observer, 直接回到。 if (CFRunLoopModeIsEmpty(currentMode)) return;
    // 1.1 布告 Observers: RunLoop 就要走入 loop。---(OB会创造释放池) CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
    // 1.2 内部函数,步入loop
    CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
    Boolean sourceHandledThisLoop = NO; int retVal = 0; do {
    // 2.1 文告 Observers: RunLoop 将在触发 Timer 回调。 CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers); // 2.2 布告 Observers: RunLoop 将要触发 Source0 (非port) 回调。 CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources); // 推行被投入的block CFRunLoopDoBlocks(runloop, currentMode);
    // 2.3 RunLoop 触发 Source0 (非port) 回调。 sourceHandledThisLoop =
    CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle); // 实施被到场的block CFRunLoopDoBlocks(runloop, currentMode);
    // 2.4 倘诺有 Source1 (基于port) 处于 ready 状态,间接管理这么些 Source1 然后跳转去管理消息。 if (
    Source0DidDispatchPortLastTime) { Boolean hasMsg = CFRunLoopServiceMachPort(dispatchPort, &msg) if (hasMsg) goto handle_msg; }
    // 3.1 如果未有待管理新闻,通告 Observers: RunLoop 的线程就要步入休眠(sleep)。--- (OB会销毁释放池并创设新释放池) if (!sourceHandledThisLoop) {
    CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting); }
    // 3.2. 调用 mach_msg 等待接受 mach_port 的音信。线程将跻身休眠, 直到被上边某二个风云唤醒。 // - 三个基于 port 的Source1 的风云。 // - 贰个 Timer 到时间了 // - RunLoop 运转时设置的最大超时时间到了 // - 被手动唤醒 CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) { mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg }
    // 3.3. 被提示,文告 Observers: RunLoop 的线程刚刚被提示了。
    CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
    // 4.0 管理新闻。 handle_msg:
    // 4.1 若是音信是Timer类型,触发那个Timer的回调。 if (msg_is_timer) { CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time()) }
    // 4.2 要是新闻是dispatch到main_queue的block,执行block。 else if (msg_is_dispatch) {
    CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE(msg); }
    // 4.3 假设音讯是Source1类型,管理那些事件 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);

    // 5.1 假设处监护人件达成,运维Runloop时设置参数为一遍性实行,设置while参数退出Runloop if (sourceHandledThisLoop && stopAfterHandle) { retVal = kCFRunLoopRunHandledSource; // 5.2 假如开发银行Runloop时设置的最小运转时间到期,设置while参数退出Runloop } else if (timeout) { retVal = kCFRunLoopRunTimedOut; // 5.3 假诺开发银行Runloop被外表调用强制结束,设置while参数退出Runloop } else if (CFRunLoopIsStopped(runloop)) { retVal = kCFRunLoopRunStopped; // 5.4 要是开行Runloop的modeItems为空,设置while参数退出Runloop } else if (CFRunLoopModeIsEmpty(runloop, currentMode)) { retVal = kCFRunLoopRunFinished; }
    // 5.5 假使没超时,mode里没空,loop也没被截至,那继续loop,回到第2步循环。 } while (retVal == 0); }
    // 6. 如若第6步决断后loop退出,通告 Observers: RunLoop 退出。--- (OB会销毁新释放池) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); }

    • 函数功能栈显示:
    {
        // 1.1 通知Observers,即将进入RunLoop
        // 此处有Observer会创建AutoreleasePool: _objc_autoreleasePoolPush();
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
        do {
    


    // 2.1 公告 Observers: 就要触发 Timer 回调。 CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION(kCFRunLoopBeforeTimers); // 2.2 公告 Observers: 将要触发 Source (非基于port的,Source0) 回调。 CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION(kCFRunLoopBeforeSources); // 执行Block CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK(block);
    // 2.3 触发 Source0 (非基于port的) 回调。 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION(source0); // 执行Block CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK(block);
    // 3.1 通告Observers,就要步向休眠 // 此处有Observer释放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush(); CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION(kCFRunLoopBeforeWaiting);
    // 3.2 sleep to wait msg. mach_msg() -> mach_msg_trap();
    // 3.3 公告Observers,线程被晋升 CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION(kCFRunLoopAfterWaiting);
    // 4.1 要是是被Timer唤醒的,回调提姆er CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION(timer);
    // 4.2 假如是被dispatch唤醒的,实践全体调用 dispatch_async 等措施放入main queue 的 block CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE(dispatched_block);
    // 4.3 如若假定Runloop是被 Source1 (基于port的) 的平地风波唤醒了,管理那么些事件 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION(source1);
    // 5. 脱离决断函数调用栈无显示 } while (...);
    // 6. 布告Observers,就要退出RunLoop // 此处有Observer释放AutoreleasePool: _objc_autoreleasePoolPop(); CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION(kCFRunLoopExit); }

    一步一步写具体的达成逻辑过于繁琐不便理解,按Runloop状态大约分为:

    1- Entry:通知OB(创建pool);
    2- 执行阶段:按顺序通知OB并执行timer,source0;若有source1执行source1;
    3- 休眠阶段:利用mach_msg判断进入休眠,通知OB(pool的销毁重建);被消息唤醒通知OB;
    4- 执行阶段:按消息类型处理事件;
    5- 判断退出条件:如果符合退出条件(一次性执行,超时,强制停止,modeItem为空)则退出,否则回到第2阶段;
    6- Exit:通知OB(销毁pool)。
    

    4、Runloop本质:mach port和mach_msg()。

    Mach是XNU的基石,进度、线程和设想内部存款和储蓄器等目标通过端口发音讯进行通讯,Runloop通过mach_msg()函数发送消息,若无port 音信,内核会将线程置于等待情形 mach_msg_trap() 。借使有消息,判定音讯类型处管事人件,并透过modeItem的callback回调(处理事件的具体执行是在DoBlock里还是在回调里目前我还不太明白???)。

    Runloop有几个根本决断点,贰个是经过msg决定Runloop是或不是等待,多个是透过剖断退出规范来支配Runloop是不是循环。


    5、怎么样处监护人件:

    • 界面刷新:
      当UI退换( Frame变化、 UIView/CALayer 的三番五遍结构变迁等)时,或手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,那些 UIView/CALayer 就被标识为待处理。
      苹果注册了一个用来监听BeforeWaiting和Exit的Observer,在它的回调函数里会遍历全体待管理的 UIView/CAlayer 以实行实际的绘图和调治,并革新 UI 分界面。

    • 事件响应:
      当叁个硬件事件(触摸/锁屏/摇荡/加速等)发生后,首先由 IO基特.framework 生成二个 IOHID伊夫nt 事件并由 SpringBoard 接收, 随后由mach port 转载给要求的App进度。
      苹果注册了七个 Source1 (基于 mach port 的) 来收纳系统事件,通过回调函数触发Sourece0(所以UIEvent实际上是依照Source0的),调用 _UIApplicationHandle伊芙ntQueue() 进行应用内部的散发。
      _UIApplicationHandleEventQueue() 会把 IOHID伊芙nt 管理并封装成 UI伊夫nt 举办拍卖或分发,当中囊括识别 UIGesture/管理显示屏旋转/发送给 UIWindow 等。

    • 手势识别:
      如果上一步的 _UIApplicationHandle伊芙ntQueue() 识别到是一个guesture手势,会调用Cancel方法将近日的touchesBegin/Move/End 种类回调打断。随后系统将相应的 UIGestureRecognizer 标识为待管理。
      苹果注册了一个 Observer 监测 BeforeWaiting (Loop将在进入休眠) 事件,其回调函数为 _UIGestureRecognizerUpdateObserver(),其内部会博得具备刚被标志为待管理的 GestureRecognizer,并进行GestureRecognizer的回调。
      当有 UIGestureRecognizer 的变化(创设/销毁/状态更换)时,这些回调都会进展对应管理。

    • GCD任务:
      当调用 dispatch_async(dispatch_get_main_queue(), block) 时,libDispatch 会向主线程的 RunLoop 发送信息,RunLoop会被提醒,并从新闻中拿走那些block,并在回调里举办那几个block。Runloop只处理主线程的block,dispatch 到其余线程依然是由 libDispatch 管理的。

    • timer:(见上modeItem部分)

    • 互连网恳求:
      至于互连网要求的接口:最底部是CFSocket层,然后是CFNetwork将其卷入,然后是NSUQX56LConnection对CFNetwork进行面向对象的包装,NSULacrosseLSession 是 iOS7 中新增加的接口,也接纳NSUENCORELConnection的loader线程。所以依然以NSUXC60LConnection为例。
      当起始网络传输时,NSUXC60LConnection 创建了五个新线程:com.apple.NSU途锐LConnectionLoader 和 com.apple.CFSocket.private。在那之中 CFSocket 线程是管理底层 socket 连接的。NSU途锐LConnectionLoader 这些线程内部会利用 RunLoop 来收纳底层 socket 的平地风波,并经过事先增进的 Source0 文告到上层的 Delegate。1010cc时时彩经典版 9

      1010cc时时彩经典版 10


    6、应用:

    • 滑动与图片刷新;
      当tableview的cell上有须求从互连网获取的图样的时候,滚动tableView,异步线程会去加载图片,加载成功后主线程就能设置cell的图纸,但是会导致卡顿。能够让设置图片的职分在CFRunLoopDefaultMode下进展,当滚动tableView的时候,RunLoop是在 UITrackingRunLoopMode 下开展,不去设置图片,而是当停止的时候,再去设置图片。
    - (void)viewDidLoad {
      [super viewDidLoad];
      // 只在NSDefaultRunLoopMode下执行(刷新图片)
      [self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:ti inModes:@[NSDefaultRunLoopMode]];    
    }
    
    • 常驻子线程,保持子线程一向处监护人件
      为了有限支撑线程长期运行,能够在子线程中步向RunLoop,况兼给Runloop设置item,幸免Runloop自动退出。
      (void)networkRequestThreadEntryPoint:(id)__unused object {
        @autoreleasepool {
            [[NSThread currentThread] setName:@"AFNetworking"];
            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
            [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
            [runLoop run];
        }
    }
    

      (NSThread *)networkRequestThread {
        static NSThread *_networkRequestThread = nil;
        static dispatch_once_t oncePredicate;
        dispatch_once(&oncePredicate, ^{
            _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
            [_networkRequestThread start];
        });
        return _networkRequestThread;
    }
    - (void)start {
        [self.lock lock];
        if ([self isCancelled]) {
            [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
        } else if ([self isReady]) {
            self.state = AFOperationExecutingState;
            [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
         [self.lock unlock];
    
    }   }
    

    int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {

    __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {

    returnCFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);

    Boolean sourceHandledThisLoop =NO;

    }

    intretVal =0;

    /// RunLoop的实现

    do{

    int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {

    // 2.1文告Observers: RunLoop将在触发Timer回调。

    /// 首先遵照modeName找到呼应mode

    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);

    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName,false);

    // 2.2布告Observers: RunLoop将在触发Source0 (非port)回调。

    /// 假诺mode里未有source/timer/observer, 直接重临。

    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);

    if(__CFRunLoopModeIsEmpty(currentMode))return;

    //试行被到场的block

    /// 1. 通报 Observers: RunLoop 将要进入 loop。

    __CFRunLoopDoBlocks(runloop, currentMode);

    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);

    本文由1010cc时时彩经典版发布于1010cc时时彩经典版,转载请注明出处:1010cc时时彩经典版Runloop运行状态分析,的内部逻

    关键词: