RunLoop理解✨

  • 版权声明:本文为博主原创文章,未经博主允许不得转载。

一. Runloop的基本知识

1. 概念
Runloop是运动循环,不断跑圈,无限循环.
RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理。之后会介绍一下在 iOS 中,苹果是如何利用 RunLoop 实现自动释放池、延迟回调、触摸事件、屏幕刷新等功能的。
  • 作用:
保持程序的持续运行 (iOS程序一直活着的原因)

处理App中的各种事件(eg:触摸事件/定时器事件/selector事件[选择器·performSelector···])

节省CPU资源,提高程序的性能(有事做事,没事休息)

程序已启动,就开启了一个runloop无限循环,因此程序才能持续的运行
2.RunLoop对象
  1. 在iOS开发中有两套api来访问Runloop
    Foundation框架 [NSRunloop](OC)

    Core Foundation框架 [CFRunloopRef](C)```

2. NSRunLoop和CFRunLoopRef都代表着RunLoop对象,它们是等价的,可以互相转换

3. NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)


######3.Runloop与线程的关系

1.Runloop和线程的关系:一个Runloop对应着一条唯一的线程

    问题:如何让子线程不死

    回答:给这条子线程开启一个Runloop

2.Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建

3.Runloop的生命周期:在第一次获取时创建,在线程结束时销毁

###二. Runloop对象的创建

1. Foundation框架\[NSRunloop]创建Runloop对象
// 获取当前线程下的Runloop, 懒加载的形式创建
 NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
// 获取主线程下的Runloop
NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];```
  1. Core Foundation框架[CFRunloopRef]创建Runloop对象
    // 获取当前线程下的Runloop, 懒加载的形式创建
    CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
    // 获取主线程下的Runloop
    CFRunLoopRef runloop2 = CFRunLoopGetMain();```

3. Runloop运行原理图
![图片来自官网](http://upload-images.jianshu.io/upload_images/838345-faae7ec4a5fb10db.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


4.Runloop相关的类

CFRunLoopRef

CFRunloopModeRef [Runloop的运行模式]

CFRunloopSourceRef [Runloop要处理的事件源]

CFRunloopTimerRef [Timer事件]

CFRunloopObserverRef [Runloop的观察者(监听者)]```
Runloop要想跑起来,它的内部必须要有一个mode,mode中必须有source/observer/time,至少要有其中的一个.

5.CFRunloopModeRef [Runloop的五个运行模式]

    Runloop每次启动的时候只能指定一个运行模型,切换模式时必须退出当前的Runloop,再重新进入一个mode,是为了分割不同组的定时器互不影响

    kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行

    UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

    UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

    GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

    kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode

6.CFRunloopTimerRef [Timer事件]

  • 1.Timer事件


  • 2.GCD定时器
    GCD定时器必须必须保存起来才能使用
    - (void)gcdTimer
    {
     NSLog(@"+++++++++");
     // 1.创建定时器
     dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
     
     // 2.设置定时器
     /*
         第一个参数:定时器
         第二个参数:从哪个时间开始
         第三个参数:间隔时间
         第四个参数:精确度, 0代表无误差
         */
        dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
     
     // 3.定时器触发事件
        dispatch_source_set_event_handler(timer, ^{
     NSLog(@"----------");
     });
     
     // 4.开启定时器
        dispatch_resume(timer);
     
     // GCD定时器创建是个局部变量需要保存才能执行
        self.timer = timer;
    }```


- 3.CFRunloopSourceRef [Runloop要处理的事件源]

(1)以前的分法
```swift
Port-Based Sources

Custom Input Sources

Cocoa Perform Selector Sources```


(2)现在的分法

```swift
Source0:非基于Port的 (用户触发的时间)

Source1:基于Port的 (通过内核和其它线程相互发送消息)```


- 4.CFRunloopObserverRef [Runloop的观察者 (监听者)]
![](http://upload-images.jianshu.io/upload_images/838345-f15d99e192b2398a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
注意:
![](http://upload-images.jianshu.io/upload_images/838345-5ed898f1141c64c7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



- 5.Runloop运行逻辑
![](http://upload-images.jianshu.io/upload_images/838345-0e9a4fa01684f71d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/838345-a917e5bd50943a4c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


###iOS中的RunLoop

>Runloop是事件接收和分发机制的一个实现。

    Runloop提供了一种异步执行代码的机制,不能并行执行任务。

    在主队列中,Main RunLoop直接配合任务的执行,负责处理UI事件、定时器以及其他内核相关事件。

>RunLoop的主要目的:

    保证程序执行的线程不会被系统终止。   

>什么时候使用RunLoop ?

    当需要和该线程进行交互的时候才会使用Runloop.

    每一个线程都有其对应的RunLoop,但是默认非主线程的RunLoop是没有运行的,需要为RunLoop添加至少一个事件源,然后去run它。

    一般情况下我们是没有必要去启用线程的RunLoop的,除非你在一个单独的线程中需要长久的检测某个事件。

    主线程��默认有Runloop。当自己启动一个线程,如果只是用于处理单一的事件,则该线程在执行完之后就退出了。所以当我们需要让该线程监听某项事务时,就得让线程一直不退出,runloop就是这么一个循环,没有事件的时候,一直卡着,有事件来临了,执行其对应的函数。

    Runloop,正如其名所示,是线程进入和被线程用来响应事件以及调用事件处理函数的地方。需要在代码中使用控制语句实现run loop的循环,也就是说,需要代码提供while 或者 for循环来驱动run loop。

    在这个循环中,使用一个Runloop对象[NSRunloop currentRunloop]执行接收消息,调用对应的处理函数。

    Runloop接收两种源事件:input sources和timer sources。

    input sources 传递异步事件,通常是来自其他线程和不同的程序中的消息;

    timer sources(定时器) 传递同步事件(重复执行或者在特定时间上触发)。

    除了处理input sources,Runloop 也会产生一些关于本身行为的notificaiton。注册成为Runloop的observer,可以接收到这些notification,做一些额外的处理。(使用CoreFoundation来成为runloop的observer)。

>Runloop工作的特点:

    1> 当有事件发生时,Runloop会根据具体的事件类型通知应用程序作出响应;

    2> 当没有事件发生时,Runloop会进入休眠状态,从而达到省电的目的;

    3> 当事件再次发生时,Runloop会被重新唤醒,处理事件。

>提示:一般在开发中很少会主动创建Runloop,而通常会把事件添加到Runloop中。

####参考:
[官方文档](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW47)
[深入理解RunLoop](http://blog.ibireme.com/2015/05/18/runloop/)

你可能感兴趣的:(RunLoop理解✨)