runloop运行循环
目的:
- 保证程序不退出
- 负责监听所有事件
硬件-->操作系统-->应用程序-->runloop(事件传递流程)
runloop每做完一件事就进入睡眠 - RunLoop在一次循环中渲染UI
runloop的运行模式
- DefaultRunLoopMode (只要有事件,就处理)
- UITrackingRunloopMode (只有当有UI事件交互发生时,runloop才会切换到,并且该模式会优先切换)
- NSRunLoopCommonModes (这并非一种模式,仅仅只是一种占位符,表示同时处在Default和UITracking两种模式下)
runloop处理三件事:
- Source:源,输入源
- Observer:观察者,观察runloop
- Timer:定时器
RunLoop与多线程的关系
当定时器在子线程中运行时,子线程在任务运行结束后会被回收,因此定时器也无法执行。
为什么子线程会被回收呢?因为子线程中的runloop默认是不会开启循环的。
为了让子线程不会被回收,我们要开启子线程的runloop
[[NSRunLoop currentRunLoop] run]; //这是一个死循环
因此我们可以在子线程中进行耗时操作,同时开启子线程中的runLoop,使子线程保持,同时又不会影响主线程。
但是,使用上面的方式开启了runloop后也会产生问题,runLoop不会结束了。我们并不想要这种结果。于是产生了下面的方式
while(_finished) {
[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0.01f]];
}
这样一来我们可以通过控制finished这个参数来控制runLoop的保持
GCD与RunLoop的关系
看如下代码实例:
@interface ViewController ()
// 声明一个dispatch_source_t对象
@property (nonatomic, strong) dispatch_source_t timer;
@end
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_time_t start_time = DISPATCH_TIME_NOW;
dispatch_time_t interval = 1.0 * NSEC_PER_SEC;
// 定时器对象,启动时间,持续时间(纳秒),等待时间
dispatch_source_set_timer(self.timer, start_time, interval, 0);
//事件
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"----------------------%@", [NSThread currentThread]);
});
dispatch_resume(self.timer);
}
定时器全局队列中执行,即子线程中运行,此时,子线程中的runloop就已经被启动了。
处理UI线程的耗时操作
当我们在主线程加载大量图片的时候,主线程会发生卡顿。
原因在与,系统会在一次runloop中渲染UI,除了一次性加载大量内存造成的卡顿外,一次性渲染UI也会造成一定程度的卡顿现象。下面的例子里,我们可以利用RunLoop的监听回调,在runLoop中自定义执行任务。
static void callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
NSLog(@"%@", info);
}
- (void)addObserver {
CFRunLoopRef runLoop = CFRunLoopGetMain();
CFRunLoopObserverRef defaultObserver;
CFRunLoopObserverContext context = {
0,
(__bridge void *)(self),
&CFRetain,
&CFRelease,
NULL
};
defaultObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopAfterWaiting, YES, 0, &callback, &context);
CFRunLoopAddObserver(runLoop, defaultObserver, kCFRunLoopCommonModes);
CFRelease(defaultObserver);
}
kCFRunLoopAfterWaiting是观察模式,规定当RunLoop被唤醒后,执行回调。所以我们可以用各种方式(比如定时器)来唤醒runloop,每唤醒一次runloop,就执行一次回调,保证每次回调在一个独立的runloop内执行,以此来分散耗时操作。
网友评论