Runloop 是事件接收和分发机制的一个实现。Runloop 本质是个对象。
可以保活子线程,防止线程在执行完成任务后销毁。
自动释放池。
NSTimer在子线程开启一个定时器,控制定时器在特定模式下执行。
当前线程中有runloop,PerformSelector才能执行。
线程和 RunLoop 之间是一一对应的,其关系是保存在一个 Dictionary 里,key是线程,value是runloop。
子线程刚创建时并没有 RunLoop,必须手动创建。
RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)。
mian()函数中调用UIApplicationMain,会创建一个主线程,用于UI处理,为了让程序可以一直运行并接收事件,所以在主线程中开启一个runloop,让主线程常驻。
因为objc不是一门线程安全的语言所以存在多线程读写不同步的问题,如果使用加锁的方式操作系统开销很大,会耗费大量的系统资源(内存、时间片轮转、cpu处理速度…),加上上面讲到的系统事件的接收处理都在主线程,如果UI异步线程的话,还会存在同步处理事件的问题,所以多点触摸手势等一些事件要保持和UI在同一个线程相对是最优解。
当调用NSObect的performSelector:相关的时候,内部会创建一个timer定时器添加到当前线程的runloop中,如果当前线程没有启动runloop,则该方法不会被调用。
// 将接收器放入一个永久循环,在此期间,它处理来自所有连接的输入源的数据。
- (void)run; // 底层代码 while 调用第三种方式runMode:
// 运行循环直到指定的日期,在此期间它处理来自所有连接的输入源的数据。
- (void)runUntilDate:(NSDate *)limitDate; // 底层代码 while 调用第三种方式runMode:
// R运行一次循环,在指定的日期之前阻止以指定模式输入。
- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),//进入runloop循环
kCFRunLoopBeforeTimers = (1UL << 1),//即将处理timer事件
kCFRunLoopBeforeSources = (1UL << 2),//即将处理source事件
kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠(等待消息唤醒)
kCFRunLoopAfterWaiting = (1UL << 6),//休眠结束(被消息唤醒)
kCFRunLoopExit = (1UL << 7),//退出runloop循环
kCFRunLoopAllActivities = 0x0FFFFFFFU//集合以上所有的状态
};
model 主要是用来指定事件在运行循环中的优先级
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态
UITrackingRunLoopMode:ScrollView滑动时
UIInitializationRunLoopMode:启动时
NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
苹果公开提供的 Mode 有两个:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)
开启该线程的runloop
条件锁
GCD
NSOperation(底层GCD实现)
iOS触摸事件全家桶 - 简书
通过上图可以看出整个流程就是app启动默认会通过mach port监听端口的方式来接受收SprinaBoard转发来的IOHIDEvent事件。