iOS开发中对RunLoop的个人心得

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发中对RunLoop的个人心得相关的知识,希望对你有一定的参考价值。

 

从接触ios到现在也有将近两年了,对iOS中的RunLoop也有了一定的认识,下面讲讲个人对RunLoop的理解

 

  初识RunLoop

 

 RunLoops是与线程相关联的基础部分,一个Run Loop就是事件处理循环,他是用来调度和协调接收到的事件处理。使用RunLoop的目的,就是使的线程有工作需要做的时候忙碌起来,当没事做的时候,又可以使得线程休眠。

我们一般程序就是执行一个线程,是一条直线.有起点终点.而runloop就是一直在线程上面画圆圈,一直在跑圈,除非切断否则一直在运行。网上说的比喻很好,直线就像昙花一现一样,圆就像OS,一直运行直到你关机为止。

 

  RunLoop资料

 

    苹果官方文档:

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html

     

     CFRunLoopRef是开源的:

http://opensource.apple.com/source/CF/CF-1151.16/

 

 

 

  RunLoop对象

 

   iOS中有两套API来访问和使用RunLoop

 

  • Foundation框架 --> NSRunLoop
  • Core Foundation框架 -->CFRunLoopRef

 

NSRunLoop和CFRunLoopRef都代表着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC包装, 所以要了解RunLoop内部结构, 需要多研究CFRunLoopRef层面的API(Core Foundation层面)

 

 

 

   RunLoop与线程

 

  • 每条线程都有唯一的一个与之对应的RunLoop对象(如果我也想开一个子线成,并且让线程不死,则子线程开一个RunLoop)
  • 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁

 

    

   获得RunLoop对象

 

  • Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
  • Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象

    如果在主线程中: 当前线程的RunLoop对象和主线程的RunLoop对象取得的是相同的。

 

   CFRunLoopSourceRef

  • CFRunLoopModeRef代表RunLoop的运行模式
  • 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer
  • 每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode(可以获取到[NSRunLoop currentRunLoop].currentMode)
  • 如果需要切换Mode,只能退出Loop再重新指定一个Mode进入(因为RunLoop是一个运行循环, 一直在跑圈, 换另一个模式,必须先退出, 然后按照另一个模式跑圈)
  • 这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响(切换模式是为了,让它按照另一个模式的Source,Timer,Observer来跑圈, 互不影响)

RunLoop 启动必须要传入一个模式,RunLoop有多个模式, 但是每次只能运行一种模式

   

     RunLoop定义两个Version的Source

  • Source0:处理App内部事件,App自己负责管理(触发),如UIEvent,CFSocket
  • Source1:由RunLoop和内核管理,Mach port驱动 如CFMach、CFMessage

 

  CFRunLoopObserverRef

      向内部报告RunLoop当前状态的更改 CAAnimation

      可以监听的时间点如下几点:

      技术分享

   

RunLoopObserver 与 Autorelease Pool

   

UIKit通过RunLoopObserver在RunLoop两次Sleep间对AutoreleasePool进行pop和push,将这次Loop中产生的Autorelease对象释放。(好像swift中没有关于释放的问题)

  

CFRunLoopModeRef

      

RunLoop在同一时段只能且必须在一种特定Mode下Run
更换Mode时, 需要暂停当前的Loop,然后重启新的Loop

NSDefalutRunLoopMode? ? ? 默认状态.空闲状态
UITrackingRunLoopMode? ? ?滑动ScrollView
UIInitializationRunLoopMode? ? 私有,App启动时
NSRunLoopCommonModes? ? ?默认包括上面第一和第二

  

 CFRunLoopTimerRef

  • CFRunLoopTimerRef是基于时间的触发器(基本上说的就是NSTimer), 一个模式下可以有多个Timer(Arrar中存放)
 /**
     *  这个方法内部实现是: 创建timer,添加到RunLoop中的默认的Mode中,RunLoop启动这个mode,取出这个mode中timer来用
     */
    [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(run) userInfo:nil repeats:YES];

    /**
     *  上面的代码等同于下面的
     */
    // 创建Timer
    NSTimer *timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(run) userInfo:nil repeats:YES];

    // 定时器只运行在 NSDefaultRunLoopMode 模式下, 一旦RunLoop进入其他模式,这个定时器就不会工作
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];



    // 如果拖动时, 我们将定时器添打上这个NSRunLoopCommonModes的标记
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

     NSLog(@"-----------%@", [NSRunLoop currentRunLoop]);
   /**
     *  定时器会跑在标记为common modes的模式下(这个模式只是个标记)
     *  RunLoop会寻找带有common标签的模式,有这个标签的,都可以跑
     *  打印当前的RunLoop信息输出为:(有common modes标签的有两个,UITrackingRunLoopMode和kCFRunLoopDefaultMode),所以定时器可以在这两个模式下跑, RunLoop只会运行一种模式

     common modes = <CFBasicHash 0x7fb8b2700490 [0x10ec6ba40]>{type = mutable set, count = 2,
     entries =>
     0 : <CFString 0x10fba2210 [0x10ec6ba40]>{contents = "UITrackingRunLoopMode"}
     2 : <CFString 0x10ec8c5e0 [0x10ec6ba40]>{contents = "kCFRunLoopDefaultMode"}
     }
     */

 

 AFNetWorking 中创建RunLoop

     

 [[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefalutRunLoopMode]//一直活着
[runLoop run];

    这在处理网络响应是一个很好的方法

 

  UITrackingRunLoopMode 与 NSTimer

 

  

// 默认情况下NSTimer被加入NSDefalutRunLoopMode
//如果想NSTimer受到组件或者动画影响 添加到NSRunLoopCommonModes

 [[NSRunLoop currentRunLoop]addTimer:timer...forMode:NSRunLoopCommonModes];

 

RunLoop的处理逻辑是什么呢?

  

    每次运行run loop,你线程的run loop对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:

  1. 通知观察者run loop已经启动
  2. 通知观察者任何即将要开始的定时器
  3. 通知观察者任何即将启动的非基于端口的源
  4. 启动任何准备好的非基于端口的源
  5. 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
  6. 通知观察者线程进入休眠
  7. 将线程置于休眠直到任一下面的事件发生:
    • 某一事件到达基于端口的源
    • 定时器启动
    • Run loop设置的时间已经超时
    • run loop被显式唤醒
  8. 通知观察者线程将被唤醒。
  9. 处理未处理的事件
    • 如果用户定义的定时器启动,处理定时器事件并重启run loop。进入步骤2
    • 如果输入源启动,传递相应的消息
    • 如果run loop被显式唤醒而且时间还没超时,重启run loop。进入步骤2
  10. 通知观察者run loop结束。

   

好的文章都是值得反复看的,我们在不同的阶段来相同的文章或资料都能有不同的收获,
提高自己对知识的理解,声明一下:最好是自己理解后再总结一次,不要一味的收藏,
每个程序员都有着成为大牛的潜质,只在是否努力。加油 技术宅们!

  

   


 

  

 





以上是关于iOS开发中对RunLoop的个人心得的主要内容,如果未能解决你的问题,请参考以下文章

个人在重构代码中的心得体会

RunLoop总结:RunLoop的应用场景

iOS开发之RunLoop--转

李洪强iOS开发之RunLoop的原理和核心机制

iOS开发RunLoop学习:一:RunLoop简单介绍

iOS开发RunLoop学习:四:RunLoop的应用和RunLoop的面试题