- RunLoop简单概述
- RunLoop相关类
- RunLoop逻辑处理
- RunLoop实践
RunLoop简单概述
-
RunLoop概念
- 字面意思:运行循环
- 内部实现:内部是由do-while循环实现的
-
RunLoop作用
- 保证程序持续运行
- 处理APP的各种事件(滑动,定时器,selector)
- 节省CPU资源,提高程序性能
如果没有RunLoop
以main函数入口为例
int main(int argc, char * argv[]) {
NSLog(@"execute main function");//程序开始
return 0;//程序结束
}
类似OC程序,执行完相应的代码之后,程序杀死,不能保证APP的持续运行
- 如果有RunLoop
以main函数入口为例
int main(int argc, char * argv[]) {
do {
NSLog(@"execute main function");//程序开始
} while(1);
return 0;//程序结束
}
相当于程序内部有一个死循汗,保证程序运行不会中断
- main函数中的RunLoop
以main函数入口为例
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
在UIApplicationMain函数内部就启动了一个RunLoop,所以UIApplicationMain函数就一直没有返回,保持了程序的持续运行
注意:默认启动的RunLoop是跟主线程相关联的,主要处理与主线程相关的事件
RunLoop循环图
-
RunLoop对象
在iOS中有2套API来访问和使用RunLoop- Foundation
NSRunLoop(OC语言) - Core Foundation
CFRunLoopRef(C语言)
- Foundation
NSRunLoop和CFRunLoopRef都是代表RunLoop,其间的联系是NSRunLoop是基于CFRunLoopRef的。也就是说要研究RunLoop的话,还是需要研究CFRunLoopRef
- RunLoop与线程
- 每条线程都有唯一的一个与之对应的RunLoop对象。
- 主线程的RunLoop随着程序已自动创建好,但是子线程的RunLoop需要手动创建。
- 获得主线程的RunLoop的方法是
[NSRunLoop mainRunLoop];
- 创建子线程的RunLoop的方法是
[NSRunLoop currentRunLoop];
(其原理需在CFRunLoop中查看)
注意:苹果不允许创建RunLoop,只提供上述两种获得RunLoop的方法
RunLoop相关类
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
若没有以上几个类,RunLoop是不会循环的
-
CFRunLoopModeRef
的介绍-
CFRunLoopModeRef
代表了RunLoop的运行模式。 - 一个RunLoop可以包含若干个Mode,每个Mode包含若干个Source/Timer/Observer
- 每次RunLoop启动时,只能指定其中的一个Model,这个Model被称作currentMode
- 如果需要切换Mode,需要退出RunLoop,再重新指定一个Mode进入。这样做是原因是为了分离不同组的Source/Timer/Observer,让其互不影响
-
-
CFRunLoopModeRef
类型
系统默认注册了5个Mode- **
kCFRunLoopDefaultMode
**:APP的默认Mode,通常主线程是在该Mode下运行的 - **
UITrackingRunLoopMode
**:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时,不受其他Mode影响 -
UIInitializationRunLoopMode
:在刚启动时APP进入的第一个Mode,启动完成后就不再使用 -
GSEventReceiveRunLoopMode
:接受系统事件的内部Mode,通常情况下不用 - **
kCFRunLoopCommonModes
**:这是一个系统占用的Mode,不是真正的Mode
- **
CFRunLoopTimerRef
CFRunLoopTimerRef
是基于时间的触发器
这里呢,基本上可以说是NSTimer
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self timer];
}
- (void)timer {
//自动加在RunLoop下,可以直接运行
//[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//只适用默认模式下
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//滑动
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
//CommonModes 只是一个标记
//有这个标记的模式有NSDefaultRunLoopMode UITrackingRunLoopMode
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)run {
NSLog(@"===run===");
}
-
CFRunLoopSourceRef
CFRunLoopSourceRef
是事件源,也可称为输入源-
按官方文档分类的话,可以分为3类
- Port-Based Sources从其他线程或内核发出的
- Custom Input Sources自定义的
- Cocoa Perform Selector Sources
-
按函数调用栈分类,可分为2类
- Sources0:非基于Port的
- Sources1:基于Port,通过其他线程或者内核通信,接收,分发系统事件
-
-
CFRunLoopObserverRef
CFRunLoopObserverRef
是观察者,能够监听RunLoop的状态的改变
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),//即将进入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1),//即将进入Timer
kCFRunLoopBeforeSources = (1UL << 2),//即将处理Sources
kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6),//即将从休眠中唤醒
kCFRunLoopExit = (1UL << 7),//即将退出RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU//活跃中
};
- (void)observer {
//[self addObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(nullable void *)#>];
//如果给RunLoop添加观察者 需要CF类
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"===%lu===", activity);
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
}
RunLoop逻辑处理
- (void)performSelector {
[self performSelector:@selector(run) withObject:nil afterDelay:2.0 inModes:@[NSRunLoopCommonModes]];
}