RunLoop

  • 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语言)

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 *)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逻辑处理

RunLoop_第1张图片
RunLoop的处理逻辑.png
RunLoop_第2张图片
RunLoop的结构.png

图片来源


- (void)performSelector {
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0 inModes:@[NSRunLoopCommonModes]];
}

你可能感兴趣的:(RunLoop)