runloop

作者: Cass__ | 来源:发表于2019-03-10 21:47 被阅读1次

    runloop

    • 一个运行循环,保证程序不退出
    • 负责处理各种事件(source、timer、observer)
    • 没有事件处理则进入休眠,节省资源,有事件则唤醒处理

    Core Foundation中关于RunLoop的5个类:
    1、CFRunLoopRef - 获得当前RunLoop和主RunLoop
    2、CFRunLoopModeRef RunLoop - 运行模式,只能选择一种,在不同模式中做不同的操作
    3、CFRunLoopSourceRef - 事件源,输入源
    4、CFRunLoopTimerRef - 定时器时间
    5、CFRunLoopObserverRef - 观察者

    runloop 源

    1、输入源:

    • NSPort 基于端口的源
    • 自定义源
    • performSelector:OnThread

    2、时间源

    runloop mode

    • 一个runloop可以有多个mode,每个mode又包含若干个source、timer、observer。
    • runloop有5个mode:
    1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
    2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
    4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
    5. kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode

    对外暴露的Mode为:NSDefaultRunLoopModeNSRunLoopCommonModes

    Q:由于 UITabelView 在滑动的时候,会从当前的 RunLoop 默认的模式 kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 自动切换到 UITrackingRunLoopMode界面追踪模式。这个时候,处于 NSDefaultRunLoopMode 里面的 NSTimer 由于切换了模式造成计时器无法继续运行。
    A:
    1、更改RunLoop运行Mode(NSRunLoopCommonModes)
    2、将NSTimer放到新的线程中

    runloop source

    • Port-Based Sources (端口)
    • Custom Input (自定义事件)
    • Cocoa Perform Selector Sources

    按照函数的调用栈:
    Source0:非基于Port的 用于用户主动触发的事件(点击button 或点击屏幕)
    Source1:基于Port的 通过内核和其他线程相互发送消息(与内核相关)
    注意:Source1在处理的时候会分发一些操作给Source0去处理

    runloop observer

    CFRunLoopObserverRef观察者,监听runloop的状态。通过回调接收状态变化。它不属于runloop的事件源。

    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
    };
    

    runloop 应用

    • 常驻线程
    • AsyncDisplayKit, RunLoop 中添加一个 Observer,监听了runloop状态进行处理渲染
    • 卡顿监控(同上)
    • 子线程定时器
    • performSelector:
      当调用 NSObject 的 performSelecter:afterDelay:后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。
      当调用 performSelector:onThread:时,实际上其会创建一个 Timer 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该方法也会失效。

    AutoreleasePool

    App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。

    第一个Observer监视Entry(即将进入runloop),其回调会调用_objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。

    第二个Observer监视BeforeWaiting和Exit。
    BeforeWaiting(准备进入休眠)时调用 _objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;
    Exit(即将退出runloop)时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

    Q:自动释放池什么时候释放?
    A:在runloop休眠前释放(kCFRunLoopBeforeWaiting)

    相关文章

      网友评论

          本文标题:runloop

          本文链接:https://www.haomeiwen.com/subject/nrvupqtx.html