runloop笔记

2018-12-15  本文已影响0人  TAsama

runloop运行循环

目的:
  1. 保证程序不退出
  2. 负责监听所有事件
    硬件-->操作系统-->应用程序-->runloop(事件传递流程)
    runloop每做完一件事就进入睡眠
  3. RunLoop在一次循环中渲染UI

runloop的运行模式

  1. DefaultRunLoopMode (只要有事件,就处理)
  2. UITrackingRunloopMode (只有当有UI事件交互发生时,runloop才会切换到,并且该模式会优先切换)
  3. NSRunLoopCommonModes (这并非一种模式,仅仅只是一种占位符,表示同时处在Default和UITracking两种模式下)
runloop处理三件事:
  1. Source:源,输入源
  2. Observer:观察者,观察runloop
  3. 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内执行,以此来分散耗时操作。

上一篇下一篇

猜你喜欢

热点阅读