美文网首页
Runloop记录

Runloop记录

作者: 唐tttyyy | 来源:发表于2020-04-12 13:53 被阅读0次

流程

SetupThisRunLoopRunTimeOutTimer();
do {
        __CFRunLoopDoObservers(kCFRunLoopBeforeTimers);
        __CFRunLoopDoObservers(kCFRunLoopBeforeSources);
       __CFRunLoopDoBlocks();
        __CFRunLoopDoSource0(); // 处理source0事件,UIEvent事件,比如触屏点击

        CheckIfExitMessagesInMainDispatchQueue(); // 检查是否有分配到主队列中的任务

        __CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);
        var wakeUpPort = SleepAndWaitForWakingUpPorts(); // 开始休眠,等待ma ch_msg事件
        
        // mach_msg_trap
        // ZZz.....   sleep
        // Received mach_msg,  wake up
        
        __CFRunLoopDoObservers(kCFRunLoopAfterWaiting); // 被事件唤醒
        // Handle msgs
        if (wakeUpPort == timePort) { // 被唤醒的事件是timer
              __CFRunLoopDoTimers(); 
        } else if (wakePort == mainDispatchQueuePort) { // 主队列有调度任务
              __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__();
        } else { // source1事件,UI刷新,动画显示
              __CFRunLoopDoSource1();
        }
        __CFRunLoopDoBlocks();
        } while (!stop && !timeout)

添加 Observer 可以监听到 RunLoop 的各种状态

kCFRunLoopEntry: 进入 RunLoop 循环

kCFRunLoopBeforeTimers: 处理 定时器(Timers)

kCFRunLoopBeforeSources: 处理 Sources

kCFRunLoopBeforeWaiting: 休眠之前

kCFRunLoopAfterWaiting: 被唤醒

kCFRunLoopExit: 退出当前 RunLoop

其中检测卡顿的话 检测 kCFRunLoopBeforeSources 以及 kCFRunLoopAfterWaiting 这2个状态, 因为干活的都是在这2个状态之后。

image.png
image.png

当UI改变( Frame变化、 UIView/CALayer 的继承结构变化等)时,或手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理。
苹果注册了一个用来监听BeforeWaiting和Exit的Observer,在它的回调函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。

当一个硬件事件(触摸/锁屏/摇晃/加速等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收, 随后由mach port 转发给需要的App进程。

苹果注册了一个 Source1 (基于 mach port 的) 来接收系统事件,通过回调函数触发Sourece0(所以UIEvent实际上是基于Source0的),调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。

_UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。

响应链是从最合适的view开始传递,处理事件传递给下一个响应者,响应者链的传递方法是事件传递的反方法,如果所有响应者都不处理事件,则事件被丢弃。我们通常用响应者链来获取上几级响应者,方法是UIResponder的nextResponder方法。
事件的分发和传递。

在开发中,我们可以使用Xcode自带的Instruments工具的Core Animation来对APP运行流畅度进行监控,使用FPS这个值来衡量。这个工具我们只能知道哪个界面会有卡顿,无法知道到底是什么操作哪个函数导致的卡顿。
主线程大量的I/O操作
大量的UI绘制
主线程进行网络请求以及数据处理
离屏渲染

网上总结比较好的图


image.png
  1. 通知Observers 进入runloop
  2. 通知Observers 即将处理Timers
  3. 通知Observers 即将处理Sources
  4. 处理blocks
  5. 处理Sources0(可能会再次处理Blocks)
  6. 如果存在Sources1 就跳到第8步
  7. 通知Observers:开始休眠,等待唤醒
  8. 通知Observers:结束休眠 被某个消息唤醒
    >1 处理Timer
    >2 处理GCD AsycToMainQueue
    >3 处理Source1
  9. 处理blocks
  10. 根据前面的执行结果,决定如何操作
    >1 回到第二步
    >2 退出Loop

1> source0 用户事件 执行PerforSelector方法,假如你在主线程performSelectors一个任务到子线程,这时候就是在代码中发送事件到子线程的runloop,这时候如果子线程开启了runloop就会执行该任务,注意该performSelector方法只有在你子线程开启runloop才能执行
2> source1 苹果创建用来接受系统发出事件,当手机发生一个触摸,摇晃或锁屏等系统,这时候系统会发送一个事件到app进程(进程通信),这也就是为什么叫基于port传递source1的原因,port就是进程端口嘛,该事件可以激活进程里线程的runloop

事件响应
当一个硬件事件(触摸/锁屏/摇晃/加速等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收, 随后由mach port 转发给需要的App进程。

苹果注册了一个 Source1 (基于 mach port 的) 来接收系统事件,通过回调函数触发Sourece0(所以UIEvent实际上是基于Source0的),调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。

Autoreleasepool 跟 Runloop的关联

添加autoreleasepool 在什么状态
每个线程对应一个AutoReleasePoolPage 以 stack为节点的 双向链表
  void *pool = AutoReleasePoolPage::push();

//some code

AutoReleasePoolPage::pop(pool);

如果对象直接被autoreleasepool包住,那在autoreleasepool大括号结束的时候就release;
如果对象不是被autoreleaspool包住,释放是由runloop控制的。在所属的runloop循环中,runloop休眠之前调用release

image.png
  • next 指向下一个能存放autorelease对象的地址

objc_autoreleasePoolPush方法调用时候,会将一个POOL_BOUNDARY入栈(到AutoreleasePoolPage中),并且返回其存放的内存地址(即下面的atautoreleasepoolobj)
调用pop方法时传入一个POOL_BOUNDARY的内存地址(即下面的atautoreleasepoolobj),会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
autoreleasepoolObj = objc_autoreleasePoolPush()
objc_autoreleasePoolPop(atautoreleasepoolobj)

有多个autoreleasepool嵌套,如果一个poolPage还有存储空间,多个autoreleasepool的对象也会放到同一个poolPage中

Page(hot):当前使用的page;Page(cold):不是当前使用的page

第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
第2个Observer
监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()

相关文章

  • RunLoop学习总结

    通过以下文章学习记录 关于Runloop的原理探究及基本使用 深入理解RunLoop RunLoop完全指南 Ru...

  • RunLoop 记录

    深入理解RunLoop

  • Runloop记录

    流程 添加 Observer 可以监听到 RunLoop 的各种状态 kCFRunLoopEntry: 进入 Ru...

  • iOS RunLoop理解

    在网上看到一篇对RunLoop讲述挺好的文章,在此记录一下深入理解RunLoop

  • runloop学习记录

    1.runloop源码 源码 2.对象介绍 接下来的对象介绍,可以借助这个图理清对象间的关系 2.1. CFRun...

  • iOS Runloop

    这里记录下iOS中Runloop相关的知识点,以备以后复习总结。 先来说下Runloop相关的概念: Runloo...

  • RunLoop详解

    写在前面 本文仅是自己学习RunLoop的一个记录,参考了ibireme大神的 深入理解RunLoop[https...

  • RunLoop 相关

    谨以此篇总结归纳记录iOS中一大知识点 RunLoop 的相关知识,以作备忘。 iOS中,提供了两种runloop...

  • RunLoop

    RunLoop简单概述 RunLoop相关类 RunLoop逻辑处理 RunLoop实践 RunLoop简单概述 ...

  • 2019 iOS面试题-----RunLoop数据结构、RunL

    RunLoop概念 RunLoop的数据结构 RunLoop的Mode RunLoop的实现机制 RunLoop与...

网友评论

      本文标题:Runloop记录

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