iOS RunLoop

RunLoop概念

  • RunLoop是通过内部维护的事件循环(Event Loop)来对事件/消息进行管理的一个对象
  • 没有消息处理时,休眠以避免资源占用;有消息需要处理时,立刻被唤醒
  1. 为什么main函数不会退出
    1
    2
    3
    4
    5
    int main(int argc, char * argv[]) {
    @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    }
    UIApplicationMain内部默认开启了主线程的RunLoop,并执行了一段无限循环的代码(不是简单的for循环while循环UIApplicationMain函数一直没有返回,不断地接收处理消息以及等待休眠,所以运行程序之后,会保持持续运行状态

RunLoop结构体

  • Source1 : 基于Port的线程间通信
  • Source0 : 触摸事件、PerformSelector
  • Timer : 定时器
  • Observer : 监听器,用于监听RunLoop的状态

RunLoop和线程

  • 线程和RunLoop是一一对应的,其映射关系是保存在一个全局的Dictionary里,线程作为keyRunLoop作为value
  • 自己创建的线程默认是没有开启RunLoop
  • runloop在第一次获取时被创建,在线程结束时被销毁
  • 对于主线程来说,runloop在程序一启动就默认创建好了
  • 对于子线程来说,runloop是懒加载的,只有当我们使用的时候才会创建,所以在子线程用定时器要注意:确保子线程的runloop被创建,不然定时器不会回调
  1. 怎么创建一个常驻线程
  • 为当前线程开启一个RunLoop(第一次调用[NSRunLoop currentRunLoop]方法时,实际是会先去创建一个RunLoop
  • 向当前RunLoop中添加一个Port/Source等维持RunLoop的事件循环(如果RunLoopmode中一个item都没有,RunLoop会退出)
  • 启动该RunLoop
    1
    2
    3
    4
    5
    @autoreleasepool {
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runLoop run];
    }
  1. 输出下边代码的执行顺序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    NSLog(@"1");

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"2");
    [self performSelector:@selector(test) withObject:nil afterDelay:10];
    NSLog(@"3");
    });

    NSLog(@"4");

    - (void)test{
    NSLog(@"5");
    }
    答案是1423test方法并不会执行
    原因是:如果是带afterDelay的延时函数,会在内部创建一个NSTimer,然后添加到当前线程的RunLoop中,也就是如果当前线程没有开启RunLoop,该方法会失效
    那么我们改成:
    1
    2
    3
    4
    5
    6
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"2");
    [[NSRunLoop currentRunLoop] run];
    [self performSelector:@selector(test) withObject:nil afterDelay:10];
    NSLog(@"3");
    });
    test方法依然不执行
    原因是:如果RunLoopmode中一个item都没有,RunLoop会退出
    即在调用RunLooprun方法后,由于其mode中没有添加任何item去维持RunLoop的事件循环,RunLoop随即还是会退出,所以我们自己启动RunLoop,一定要在添加item
    1
    2
    3
    4
    5
    6
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"2");
    [self performSelector:@selector(test) withObject:nil afterDelay:10];
    [[NSRunLoop currentRunLoop] run];
    NSLog(@"3");
    });
-------------本文结束感谢您的阅读-------------
最近的文章

iOS 如何优化 App 的启动耗时

iOS 的 App 启动时长大概可以这样计算: t(App 总启动时间) = t1(main 调用之前的加载时间) + t2(main 调用之后的加载时间) t1 = 系统 dylib(动态链接库)和自身 App 可执行文件的加载 t2 = main方法执行之后到AppDelegate类中的appl …

继续阅读
更早的文章

iOS 多线程相关之performSelector、死锁

performSelector12345678910//在当前线程延迟1s执行,响应了OC语言的动态性:延迟到运行时才绑定方法[self performSelector:@selector(aaa) withObject:nil afterDelay:1];/&#x2 …

继续阅读