RunLoop–运行环
- RunLoop的基本作用
- 保持程序的持续运行
- 处理app中的各种事件(比如触摸事件,定时器事件,selector事件)
- 节省CPU资源,提高程序性能:该做事的时候做事,该休息就休息
- main函数中的RunLoop
- UIApplication函数内部就启动了一个RunLoop
- 这个Runloop主线程的,默认是开启的,子线程的RunLoop默认是关闭的
- 每条线程都有唯一与之对应的RunLoop对象
- ios中2套API来访问和使用RunLoop
- NSRunLoop(NSRunLoop -> oc)
- CFRunLoopRef(Core Foundation -> c)
- RunLoop对象的获取
- Foundation中
objc
[NSRunLoop currentRunLoop];//获取当前线程的RunLoop对象
[NSRunLoop mainRunLoop];//获取主线程的RunLoop对象
- Core Foundation中
objc
CFRunLoopGetCurrent();//获取当前线程的RunLoop
CFRunLoopGetMain();//获取主线程的RunLoop对象
- Foundation中
RunLoopMode 系统默认注册了5个mode
kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
- CFRunLoopObserverRef
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity)
{
kCFRunLoopEntry = (1UL << 0), //即将进入loop
kCFRunLoopBeforeTimers = (1UL << 1),//即将处理Timer
kCFRunLoopBeforeSources = (1UL << 2),//即将处理Source
kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6),///刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7),//即将推出loop
kCFRunLoopAllActivities = 0x0FFFFFFFU//
};
- RunLoop处理逻辑
- 监听RunLoop的状态
// 创建observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"----监听到RunLoop状态发生改变---%zd", activity);
});
// 添加观察者:监听RunLoop的状态
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// 释放Observer
CFRelease(observer);
- GCD定时器
- 优势:不受RunLoop的影响,计时更加准确,稳定性好
// 获得队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// dispatch_queue_t queue = dispatch_get_main_queue();
// 创建一个定时器(dispatch_source_t本质还是个OC对象)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 设置定时器的各种属性(几时开始任务,每隔多长时间执行一次)
// GCD的时间参数,一般是纳秒(1秒 == 10的9次方纳秒)
// 何时开始执行第一个任务
// dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC) 比当前时间晚3秒
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer, start, interval, 0);
// 设置回调
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"------------%@", [NSThread currentThread]);
count++;
// if (count == 4) {
// // 取消定时器
// dispatch_cancel(self.timer);
// self.timer = nil;
// }
});
// 启动定时器
dispatch_resume(self.timer);
RunLoop的应用
- NSTimer
- ImageView显示
- 就是在滑动界面的时候停止图片的刷新,保证了界面的流畅性
- PerformSelector
- 常驻线程
- 自动释放池
常驻线程
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[XMGThread alloc] initWithTarget:self selector:@selector(execute) object:nil];
[self.thread start];
}
- (void)execute
{
NSLog(@"----------run----%@", [NSThread currentThread]);
//默认线程子线程处理完事件之后机会被回收,但通过向当前的RunLoop添加port之后,使之能够一直运行,保证当前线程不死.RunLoop有Timer,source才能运行
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"---------");
}
- (void)test
{
NSLog(@"----------test----%@", [NSThread currentThread]);
}
- (void)test2
{
NSLog(@"***********test2*******%@", [NSThread currentThread]);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(test2) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- RunLoop和自动释放池
kCFRunLoopEntry; // 创建一个自动释放池
kCFRunLoopBeforeWaiting; // 销毁自动释放池,创建一个新的自动释放池
kCFRunLoopExit; // 销毁自动释放池
- ImageView显示
// 只在NSDefaultRunLoopMode模式下显示图片
[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"placeholder"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]];