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