iOS之Runloop

2016-04-11  本文已影响35人  搁浅的青蛙

Runloop基本知识

什么是Runloop

Runloop,正如其名,loop表示某种循环,和run放在一起就表示一直在运行着的循环。

Runloop的作用

Runloop对象

Runloop与线程

  1. Runloop和线程的关系:一个Runloop对应着一条唯一的线程。为了让子线程不死,可以给这条子线程开启一个Runloop
  2. Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建
  3. Runloop的生命周期:在第一次获取时创建,在线程结束时销毁

如何获取Runloop对象

  1. 获得当前Runloop对象
//01 NSRunloop 
NSRunLoop * runloop1 = [NSRunLoop currentRunLoop]; 
//02 CFRunLoopRef 
CFRunLoopRef runloop2 = CFRunLoopGetCurrent(); 
  1. 拿到当前应用程序的主Runloop(主线程对应的Runloop)
//01 NSRunloop 
NSRunLoop * runloop1 = [NSRunLoop mainRunLoop]; 
//02 CFRunLoopRef
 CFRunLoopRef runloop2 = CFRunLoopGetMain(); 
  1. 注意点:开一个子线程创建runloop,不是通过alloc init方法创建,而是直接通过调用currentRunLoop方法来创建,它本身是一个懒加载的。
  2. 在子线程中,如果不主动获取Runloop的话,那么子线程内部是不会创建Runloop的。可以下载CFRunloopRef的源码,搜索_CFRunloopGet0,查看代码。
  3. Runloop对象是利用字典来进行存储,而且key是对应的线程Value为该线程对应的Runloop。

Runloop相关类

Runloop和相关类之间的关系图

Runloop和相关类之间的关系图
//NSTimer 调用了scheduledTimer方法,那么会自动添加到当前的runloop里面去,而且runloop的运行模式kCFRunLoopDefaultMode
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//更改模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//定时器添加到UITrackingRunLoopMode模式,一旦runloop切换模式,那么定时器就不工作
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
//定时器添加到NSDefaultRunLoopMode模式,一旦runloop切换模式,那么定时器就不工作
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//占位模式:common modes标记
//被标记为common modes的模式 kCFRunLoopDefaultMode  UITrackingRunLoopMode
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

GCD中的定时器

//0.创建一个队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.创建一个GCD的定时器
/*
第一个参数:说明这是一个定时器
第四个参数:GCD的回调任务添加到那个队列中执行,如果是主队列则在主线程执行
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//2.设置定时器的开始时间,间隔时间以及精准度
//设置开始时间,三秒钟之后调用
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,3.0 *NSEC_PER_SEC);
//设置定时器工作的间隔时间
uint64_t intevel = 1.0 * NSEC_PER_SEC;
/*
第一个参数:要给哪个定时器设置
第二个参数:定时器的开始时间DISPATCH_TIME_NOW表示从当前开始
第三个参数:定时器调用方法的间隔时间
第四个参数:定时器的精准度,如果传0则表示采用最精准的方式计算,如果传大于0的数值,则表示该定时切换i可以接收该值范围内的误差,通常传0
该参数的意义:可以适当的提高程序的性能
注意点:GCD定时器中的时间以纳秒为单位(面试)
*/
dispatch_source_set_timer(timer, start, intevel, 0 * NSEC_PER_SEC);
//3.设置定时器开启后回调的方法
/*
第一个参数:要给哪个定时器设置
第二个参数:回调block
*/
dispatch_source_set_event_handler(timer, ^{
NSLog(@"------%@",[NSThread currentThread]);
});
//4.执行定时器
dispatch_resume(timer);
//注意:dispatch_source_t本质上是OC类,在这里是个局部变量,需要强引用
self.timer = timer;
//创建一个runloop监听者
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"监听runloop状态改变---%zd",activity);
    });
    //为runloop添加一个监听者
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    CFRelease(observer);
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),   //即将进入Runloop
        kCFRunLoopBeforeTimers = (1UL << 1),    //即将处理NSTimer
        kCFRunLoopBeforeSources = (1UL << 2),   //即将处理Sources
        kCFRunLoopBeforeWaiting = (1UL << 5),   //即将进入休眠
        kCFRunLoopAfterWaiting = (1UL << 6),    //刚从休眠中唤醒
        kCFRunLoopExit = (1UL << 7),            //即将退出runloop
        kCFRunLoopAllActivities = 0x0FFFFFFFU   //所有状态改变
};

Runloop运行逻辑

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

  1. 通知观察者runloop已经启动
  2. 通知观察者任何即将要开始的定时器
  3. 通知观察者任何即将启动的非基于端口的源
  4. 启动任何准备好的非基于端口的源
  5. 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9
  6. 通知观察者线程进入休眠
  7. 将线程置于休眠知道任一下面的事件发生:
  1. 通知观察者线程将被唤醒
  2. 处理未处理的事件
  1. 通知观察者runloop结束


    Runloop运行逻辑

Runloop应用

Runloop参考资料

上一篇 下一篇

猜你喜欢

热点阅读