1.RunLoop

     

第一篇学习就从这里开始吧。

     

从这个阶段开始,真的需要程序员开始背概念,不能只写代码,考虑实现,更多的要开始学会从底层开始学习,我醒悟的时间太短了,导致四年的时间可能大部分都浪费了。每周深入研究一个课题,一年之后你会完全不一样的,这是一个iOS架构师朋友给我的诚恳建议,拿出来给大家分享,就算工作时间很忙,也要坚持这件事情。

     

Runloop是面试中几乎必问的问题了,我在曾经的开发中有简易使用为了优化项目,但是从来没有背过什么基础概念,其实这个是不对的想法,程序员依然需要背,是为了在进阶道路上能随时想到解决方案,提高问题应对的敏感度。这篇是参考很多的资料,加上自己使用过程中的理解,按照自己的思路和顺序来总结的,如果有朋友觉得可以参考,记得看完之后一定要按照自己的想法重新总结,别人的和自己的永远都有区别,而且别人的东西不太容易背~啊哈哈。

      

     

1.什么是runloop

     

Runloop是线程相关的基础框架的一部分,作用是可以让线程常驻,让线程在有工作的时候忙,在没有工作的时候处于休眠状态。内部是do-while循环,判断source1是否有任务,如果没有任务,线程会进入休眠,判断runloop中是否有item,如果没有,会通知observer推出loop。

   

2.runloop和线程的关系

   

runloop和线程是紧密相连的,runloop如果没有线程,就失去了存在的意义(runloop和线程是否是一一对应的?)。每个线程包括主线程,都有与之相对应的runloop对象。主线程的runloop是默认启动的,UIApplicationMain()函数内部会为mainthread设置一个NSRunLoop对象,可以让我们的应用在无人操作的时候休息,需要干活的时候可以随时响应。对于其他线程来说,runloop是没有启动的,如果更多的线程runloop交互可以手动配置和启动,如果线程只是执行一个长时间已确定的任务,则不需要常驻线程,也就不需要启动和配置runloop。

   

3.runloop对外接口

   

CoreFoundation里面关于Runloop有5类:

   CFRunLoopRef    //runloop对象

   CFRunLoopModeRef    //runloop场景

   CFRunLoopSourceRef   //runloop的source类

   CFRunLoopTimerRef    //runloop的timer类

   CFRunLoopObserverRef  //runloop的观察者

   

一个RunLoop包含若干个Mode,CFRunLoopModeRef类没有对外暴漏,直通过CFRunLoopRef的接口进行了封装。每次调用RunLoop的主函数时,只能指定其中一个currentMode。如果需要切换mode,只能退出loop,重新指定一个mode进入,主要是为了分隔开不同组的item,让其互不影响。

   

CFRunLoopTimerRef是基于时间的触发器,它和NSTimer是toll-free bridged的(某些数据类型是可以在CoreFoundation和Foundation里互换的,详细了解https://blog.csdn.net/quanqinyang/article/details/38679167)。Timer中包含一个时间长度和一个回调(函数指针),当其加入到runloop时,runloop会注册对应的时间点,当时间点到时,runloop会被唤醒以执行那个回调函数。

 

CFRunLoopObserverRef是runloop的观察者,每个observer都包含一个回调,当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
   };
   
   
sourse/timer/observer被统称为mode item,一个item可以被同时加入很多个mode,但是一个item被加入同一个mode时是不会有效果的,如果mode中没有item都没有,则runloop直接退出,不再循环。
 
4.runloop应用场景
   
4.1定时器
 
之所以把定时器放到第一个,是因为在开发实现过程中最容易出现问题,如果没有对timer进行特殊处理,在tableview滑动的时候,timer是会被暂停的(详解可以看看这篇文章 https://www.jianshu.com/p/5d17c722fefd),原因是timer的简便构造方法把timer加入了NSRunLoopDefaultMode上,而一个nstimer注册到RunLoop后,RunLoop会为其重复的时间点注册好时间,而RunLoop为了节省资源,并不会在一个非常准确的时间点回调这个Timer,如果某个时间点错过了,例如一个很长的任务,那这个时间点的回调也会跳过去,不会延后执行,而tableview在滑动时只会处理UITrackingRunLoopMode,也就是说当前的RunLoop并没有功夫处理timer事件。解决方法有很多,可以将timer放到commonMode上。
   
4.2autoreleasePool自动释放池 

app启动后,苹果会在主线程RunLoop里注册两个Observer,其回调都是——wrapRunLoopWithAutoreleasePoolHandler()。

 

第一个observer监听事件是entry(即将进入Loop),回调内调用_objc_autoreleasePoolPush()创建自动释放池。优先级最高,保证创建释放池发生在其他所有回调之前。

 

第二个observer监听了两个事件:

BeforWaiting(准备进入休眠)时调用_objc_autoreleasePoolPop()释放旧的池,调用_objc_autoreleasePoolPuch()创建新池;

Exit(即将退出Loop)时调用_objc_autoreleasePoolPop()来释放旧池。

这个observer的优先级最低,保证其释放池发生在所有回调之后。

  

posted @ 2019-12-03 16:03  灰熊Grizzly  阅读(149)  评论(0)    收藏  举报