• Runloop 运行循环


    什么是RunLoop

    • 运行循环
    • 一个线程对应一个RunLoop,主线程的RunLoop默认已经启动,子线程的RunLoop得手动启动(懒加载, 调用currentRunLoop方法)
    • RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop

    RunLoop作用

    • 保持程序的持续运行
    • 处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
    • 节省CPU资源,提高程序性能:该做事时做事,该休息时休息 ......

    模拟RunLoop内部实现

    • 其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)
    void message(int num)
    {
        printf("执行第%i个任务", num);
    }
    int main(int argc, const char * argv[]) {
        do {
            printf("有事吗? 没事我睡了");
            int number;
            scanf("%i", &number);
            message(number);
        } while (1);
        return 0;
    }
    

    获得RunLoop对象

    • RunLoop对象
      • NSRunLoop
      • CFRunLoopRef
    • Foundation(OC) objc [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象,系统会自动创建(懒加载) [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象,主线程默认就有RunLoop
    • Core Foundation(C) objc CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象 CFRunLoopGetMain(); // 获得主线程的RunLoop对象

    RunLoop结构

    RunLoop相关类

    • Core Foundation中关于RunLoop的5个类:
    • CFRunLoopRef对应RunLoop对象
      • CFRunLoopModeRef代表RunLoop的运行模式, 系统默认注册了5个Mode
        • kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
        • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
        • kCFRunLoopCommonModes(NSRunLoopCommonModes) : 这是一个占位用的Mode,不是一种真正的Mode
      • CFRunLoopSourceRef是事件源(输入源)
        • Source0:非基于Port(端口)的
        • Source1:基于Port的
      • CFRunLoopTimerRef是基于时间的触发器
        • CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响
      • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

    // 1.创建Observer
        // 第一个参数:用于分配该observer对象的内存
        // 第二个参数:用以设置该observer所要关注的的事件
        // 第三个参数:用于标识该observer是在第一次进入run loop时执行, 还是每次进入run loop处理时均执行
        // 第四个参数:用于设置该observer的优先级
        // 第五个参数: observer监听到事件时的回调block
        CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            switch(activity)
            {
                case kCFRunLoopEntry:
                    NSLog(@"即将进入loop");
                    break;
                case kCFRunLoopBeforeTimers:
                    NSLog(@"即将处理timers");
                    break;
                case kCFRunLoopBeforeSources:
                    NSLog(@"即将处理sources");
                    break;
                case kCFRunLoopBeforeWaiting:
                    NSLog(@"即将进入休眠");
                    break;
                case kCFRunLoopAfterWaiting:
                    NSLog(@"刚从休眠中唤醒");
                    break;
                case kCFRunLoopExit:
                    NSLog(@"即将退出loop");
                    break;
                default:
                    break;
            }
        });
    
        // 2.添加监听
        /*
         第一个参数: 给哪个RunLoop添加监听
         第二个参数: 需要添加的Observer对象
         第三个参数: 在哪种模式下监听
         */
        CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
    
        // 3,释放observer
        CFRelease(observer);
    
    • RunLoop处理逻辑

    RunLoop应用

    • NSTimer
      • 只能在指定的model下运行 objc NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    • ImageView显示(一般在默认的模式下设置,所以此方法了解)

      • 只能在指定的model下设置图片 objc [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"def"] afterDelay:2.0 inModes:@[UITrackingRunLoopMode]];
    • PerformSelector

      • 只能在指定的model下调用 objc [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageNamed:@"lnj"] waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];

    常驻线程

    • 注意点:默认情况下,只要一个线程的任务执行完毕,那么这个线程就不能使用了
    • [NSRunLoop currentRunLoop]仅仅只是创建了一个NSRunLoop对象, 必须调用run才会执行死循环
    • NSRunLoop的model中必须有source/timer,死循环才不会退出
      objc
      NSRunLoop *runloop = [NSRunLoop currentRunLoop];
      [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
      [runloop run];

    • 自动释放池

      • 1: 即将进入RunLoop : 创建一个自动释放池
      • 32:即将休眠 : 释放上一次的自动释放池, 创建一个新的自动释放池
      • 128:即将退出RunLoop : 释放自动释放池
    activities = 0x1 = 1
    1: 即将进入RunLoop : 创建一个自动释放池
    activities = 0xa0 = 160 = 128 + 32
    32:即将休眠 : 释放上一次的自动释放池, 创建一个新的自动释放池
    128:即将退出RunLoop : 释放自动释放池
    

     

    RunLoop(运行循环)过程总结:

    • 一个线程对应一个RunLoop,主线程的RunLoop程序启动时默认已经启动
    • 子线程的RunLoop通过查看源码(_CFRunLoopGet0( ))发现是通过字典将线程与RunLoop进行绑定,得手动启动
    • 调用currentRunLoop方法获取子线程的RunLoop,可以理解为懒加载:先判断子线程里有没有RunLoop,有就直接从字典里取,没有就创建并和线程绑定,保存到字典当中
    • RunLoop里有很多的Mode,Mode里又有很多的Source,Timer,Observer,但是只能选择一个Mode启动,这样能防止多个模式的source,timer错乱,不好控制逻辑
    • 如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop
    • 我们可以通过Timer的形式监听RunLoop的执行流程,先进入RunLoop,处理一些系统的事件,执行处理Timer,Source0,Source1...处理完后进入休眠状态
    • 当又有事件时,被唤醒,处理Timer,Source0,Source1...,再进入休眠状态
    • ...
    • RunLoop当Timer超时时会结束,当线程结束时会销毁
  • 相关阅读:
    PHP Mysql 根据一个给定经纬度的点,进行附近地点查询–算法 转载
    使用正则表达式匹配JS函数代码
    随便写点
    test
    Dat
    数据格式
    recod
    扫描
    转载 ASP.NET MVC中使用ASP.NET Identity
    制作32位和64位整合的安装包
  • 原文地址:https://www.cnblogs.com/wxdonly/p/5097580.html
Copyright © 2020-2023  润新知