1. 概念
runloop是运动循环,不断跑圈,无限循环
作用:
保持程序的持续运行 (iOS程序一直活着的原因)
处理App中的各种事件(eg:触摸事件/定时器事件/selector事件【选择器·performSelector···】)
节省CPU资源,提高程序的性能(有事做事,没事休息)
程序已启动,就开启了一个runloop无限循环,因此程序才能持续的运行
#import <UIKit/UIKit.h> #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } 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的生命周期:在第一次获取时创建,在线程结束时销毁
1. Foundation框架【NSRunloop】创建Runloop对象
// 获取当前线程下的Runloop, 懒加载的形式创建 NSRunLoop * runloop1 = [NSRunLoop currentRunLoop]; // 获取主线程下的Runloop NSRunLoop * runloop1 = [NSRunLoop mainRunLoop]; 2. Core Foundation框架【CFRunloopRef】创建Runloop对象
// 获取当前线程下的Runloop, 懒加载的形式创建 CFRunLoopRef runloop2 = CFRunLoopGetCurrent(); // 获取主线程下的Runloop CFRunLoopRef runloop2 = CFRunLoopGetMain(); 3. Runloop运行原理图
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> NSTimer
- (void)timer0 { NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES]; /* NSDefaultRunLoopMode -> kCFRunLoopDefaultMode 默认模式主线程中执行 NSRunLoopCommonModes -> kCFRunLoopDefaultMode/UITrackingRunLoopMode UITrackingRunLoopMode -> 界面追踪,其他不工作 */ [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; } 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; } 7. CFRunloopSourceRef 【 Runloop 要处理的事件源】
( 1 )以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources
( 2 )现在的分法
Source0 :非基于 Port 的 (用户触发的时间)
Source1 :基于 Port 的 ( 通过内核和其它线程相互发送消息 )
8. CFRunloopObserverRef 【 Runloop 的观察者(监听者)】
- (void)observer { // 创建一个观察者 /* 第一个参数:获取内存的方式 第二个参数:监听哪些状态 第三个参数:是否持续监听 第四个参数:0 第五个参数:block代码块 kCFRunLoopEntry = (1UL << 0), 准备进入 kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件 kCFRunLoopBeforeSources = (1UL << 2), 即将处理source事件 kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入休眠 kCFRunLoopAfterWaiting = (1UL << 6), 唤醒 kCFRunLoopExit = (1UL << 7), 退出 kCFRunLoopAllActivities = 0x0FFFFFFFU 所有 */ CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { switch (activity) { case kCFRunLoopEntry: NSLog(@"准备进入"); break; case kCFRunLoopBeforeTimers: NSLog(@"即将处理timer事件"); break; case kCFRunLoopBeforeSources: NSLog(@"即将处理source事件"); break; case kCFRunLoopBeforeWaiting: NSLog(@"即将进入休眠"); break; case kCFRunLoopAfterWaiting: NSLog(@"唤醒"); break; case kCFRunLoopExit: NSLog(@"退出"); break; } }); // 给runloop添加一个观察者 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES]; // 释放观察者 CFRelease(observer); } 9. Runloop运行逻辑