RunLoop 详解

什么是RunLoop?
从字面意思看叫做运行循环,俗称“跑圈”。

RunLoop的主要作用:
1、保持程序的持续运行
2、处理APP中的各种事件(触摸事件,定时器事件,selector事件)
3、节省CPU资源,提高程序性能:有事做事,没事休息

main函数中的RunLoop:

如果没有RunLoop:

屏幕快照 2016-10-05 下午10.40.43.png

没有RunLoop,程序执行第三行后就结束了;

如果有RunLoop:

屏幕快照 2016-10-05 下午10.46.53.png

RunLoop内部其实就是一个do-while循环,在这个循环内部不断的处理各种事件,程序不会执行第7行代码,因此保证了程序不会退出一直运行;

屏幕快照 2016-10-05 下午10.26.44.png

UIApplicationMain函数内部自动启动了一个RunLoop,所以UIApplicationMain一直没有返回,保持程序持续运行。这个默认启动的RunLoop是跟主线程相关联的;

RunLoop对象:

iOS中有2套API访问和使用RunLoop:
Foundation框架下的:NSRunLoop
CoreFoundation框架下的:CFRunLoopRef;
CFRunLoopRef和NSRunLoop都代表RunLoop对象;NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层的API;
可参考苹果官方文档:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
苹果开源文档:
http://opensource.apple.com/source/CF/CF-1151.16/

RunLoop与线程

每条线程都有唯一一个与之对应的RunLoop对象;
主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动去创建;
RunLoop在第一次获取时创建,线程销毁时销毁;

获取RunLoop对象
Foundation:

[NSRunLoop currentRunLoop];// 获得当前线程的RunLoop对象[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

Core Foundation:

CFRunLoopGetCurrent();// 获得当前线程的RunLoop对象CFRunLoopGetMain();// 获得主线程的RunLoop对象

RunLoop相关类
CoreFoundation中关于RunLoop的5个类:
CFRunLoopRef //RunLoop对象
CFRunLoopModeRef //RunLoop运行模式
CFRunLoopSourceRef //RunLoop事件源
CFRunLoopTimerRef //RunLoop定时器事件
CFRunLoopObserverRef //监听RunLoop状态的观察者

CFRunLoopModeRef(模式)

mode.png

一个RunLoop包含若干个Mode,每个Mode又包含若干个source、timer、observer;

每次RunLoop启动时,只能指定其中一个Mode,这个Mode称作CurrentMode;

如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入;这样做是为了分隔开不同组的source、timer、observer,互不影响;具体怎么退出,怎么重新指定Mode,官方文档并未讲述,我们也不得而知;

系统默认注册了5个Mode:

KCFRunLoopDefaultMode:APP的默认Mode,通常主线程在这个Mode下运行;

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

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

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

KCFRunloopCommonModes:这是一个占位用的Mode,不是真正的Mode;表示具有KCFRunloopCommonModes标记的模式;
KCFRunloopCommonModes标记的Mode有两种:
0 : {contents = "UITrackingRunLoopMode"}
2 : {contents = "kCFRunLoopDefaultMode"}

因此当一个事件是在RunLoop的KCFRunloopCommonModes模式下,RunLoop在KCFRunLoopDefaultMode和UITrackingRunLoopMode模式时都能处理该事件;

CFRunLoopSourceRef(事件源)
事件源的分类
从前:
1、Port-Based Sources
2、Custom Input Sources
3、Cocoa Perform Selector Sources

现在:
Source0:非基于Port的,用于用户主动触发的事件
Source1:基于Port的,通过内核和其他线程相互发送消息

CFRunLoopTimerRef(基于时间的触发器)
NSTimer,会受到RunLoop的Mode影响
GCD的定时器不受RunLoop的Mode影响

//1.创建NSTimerNSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];//2.添加到runloop,当runloop的运行模式为NSDefaultRunLoopMode时候,定时器工作[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];

那么timer只在NSDefaultRunLoopMode下工作;同理如果选择UITrackingRunLoopMode,timer只在UITrackingRunLoopMode下工作;选择NSRunLoopCommonModes,timer在UITrackingRunLoopMode和NSDefaultRunLoopMode下都能正常工作;

//0.创建队列dispatch_queue_t queue = dispatch_get_global_queue(0, 0);//创建一个GCD定时器/* 第一个参数:表明创建的是一个定时器 第四个参数:队列 */dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);self.timer = timer;//2.设置定时器的开始时间,间隔时间,精准度/*  第1个参数:要给哪个定时器设置  第2个参数:开始时间  第3个参数:间隔时间  第4个参数:精准度 一般为0 提高程序的性能 GCD的单位是纳秒 */dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 10 * NSEC_PER_SEC);//3.设置定时器要调用的方法dispatch_source_set_event_handler(timer, ^{    NSLog(@"GCD-timer");});//4.启动 dispatch_resume(timer);

定时器对象需要声明属性或成员变量去接收,否则这段代码执行完,timer会被销毁;

CFRunLoopObserverRef(观察者)
监听的时间点:

activity.png

//创建一个监听对象/* 第一个参数:分配存储空间的 第二个参数:要监听的状态 kCFRunLoopAllActivities 所有状态 第三个参数:是否要持续监听 第四个参数:优先级 第五个参数:回调 */ CFRunLoopObserverRef observer =  CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {    switch (activity) {        case kCFRunLoopEntry:            NSLog(@"runloop进入");            break;        case kCFRunLoopBeforeTimers:            NSLog(@"runloop要去处理timer");            break;        case kCFRunLoopBeforeSources:            NSLog(@"runloop要去处理Sources");            break;        case kCFRunLoopBeforeWaiting:            NSLog(@"runloop要睡觉了");            break;        case kCFRunLoopAfterWaiting:            NSLog(@"runloop醒来啦");            break;        case kCFRunLoopExit:            NSLog(@"runloop退出");            break;        default:            break;    }});//给runloop添加监听者/* 第一个参数:要监听哪个runloop 第二个参数:监听者 第三个参数:要监听runloop在哪种运行模式下的状态 */CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(runLoop) userInfo:nil repeats:YES];CFRelease(observer);

以上内容是我学习过程中查询资料总结出来的,如有疏漏或错误,欢迎批评指正,也欢迎大家一起交流讨论;关于RunLoop的应用,后面有空再继续补充;

文/Gradient

关键字:runloop, 产品经理

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部