• iOS Runloop 消息循环


    • 介绍

      Runloop是一种事件监听循环,可以理解成一个while死循环,监听到事件就起来,没有就休息。
      Runloop可以在不同模式下进行切换,iOS有五种模式,其中UIInitializationRunLoopModel应用程序启动时会使用,启动完成后将不再使用;GSEventReceiveRunLoopMode这个是接受系统内部的Model,通常做不到。还有UITrackingRunLoopMode、NSDefaultRunLoopMode、NSRunLoopCommonModes三种模式是我们通常用到的,下文中会详细讲解,其中NSRunLoopCommonModes是一个占位符,NSDefaultRunLoopMode和UITrackingRunLoopMode都会绑定这个占位符。

    • Runloop

    - 1.1 字面意思

    a 运行循环
    b 跑圈

    - 1.2 基本作用(作用重大)

    (1) 保持程序的持续运行(ios程序为什么能一直活着不会死)
    (2) 处理app中的各种事件(比如触摸事件、定时器事件【NSTimer】、selector事件【选择器·performSelector···】)
    (3) 节省CPU资源,提高程序性能,有事情就做事情,没事情就休息

    - 1.3 重要说明

    1)如果没有Runloop,那么程序一启动就会退出,什么事情都做不了。
    (2)如果有了Runloop,那么相当于在内部有一个死循环,能够保证程序的持续运行
    (3)main函数中的Runloop
    (4)在UIApplication函数内部就启动了一个Runloop,函数返回一个int类型的值
    (5)这个默认启动的Runloop是跟主线程相关联的

    - 1.4 Runloop对象

    1)在iOS开发中有两套api来访问Runloop
            a.foundation框架【NSRunloop】
            b.core foundation框架【CFRunloopRef】
    (2)NSRunLoop和CFRunLoopRef都代表着RunLoop对象,它们是等价的,可以互相转换
    (3)NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)

    - 1.5 Runloop参考资料

    1)苹果官方文档:
        https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html2)CFRunLoopRef开源代码下载地址:
        http://opensource.apple.com/source/CF/CF-1151.16/

    - 1.6 Runloop与线程

    1)Runloop和线程的关系:一个Runloop对应着一条唯一的线程
            问题:如何让子线程不死
            回答:给这条子线程开启一个Runloop
    (2)Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建
    (3)Runloop的生命周期:在第一次获取时创建,在线程结束时销毁

    - 1.7 获得Runloop对象

    1)获得当前Runloop对象
            a. NSRunloop
                NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
            b.CFRunLoopRef
                CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
    (2)拿到当前应用程序的主Runloop(主线程对应的Runloop)
            a.NSRunloop
                NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];
            b.CFRunLoopRef
                CFRunLoopRef runloop2 = CFRunLoopGetMain();
    (3)注意点:开一个子线程创建runloop,不是通过alloc init方法创建,而是直接通过调用currentRunLoop方法来创建,它本身是一个懒加载的。
    (4)在子线程中,如果不主动获取Runloop的话,那么子线程内部是不会创建Runloop的。可以下载CFRunloopRef的源码,搜索_CFRunloopGet0,查看代码。
    (5)Runloop对象是利用字典来进行存储,而且key是对应的线程Value为该线程对应的Runloop。

    - 1.8 Runloop相关类

    1)Runloop运行原理图 
    (2)五个相关的类
      a.CFRunloopRef
      b.CFRunloopModeRef【Runloop的运行模式】
      c.CFRunloopSourceRef【Runloop要处理的事件源】
      d.CFRunloopTimerRef【Timer事件】
      e.CFRunloopObserverRef【Runloop的观察者(监听者)】3)Runloop和相关类之间的关系图
    (4)Runloop要想跑起来,它的内部必须要有一个mode,这个mode里面必须有sourceobserver	imer,至少要有其中的一个。

    - CFRunloopModeRef

    1)CFRunloopModeRef代表着Runloop的运行模式
    (2)一个Runloop中可以有多个mode,一个mode里面又可以有多个sourceobserver	imer等等
    (3)每次runloop启动的时候,只能指定一个mode,这个mode被称为该Runloop的当前mode
    (4)如果需要切换mode,只能先退出当前Runloop,再重新指定一个mode进入
    (5)这样做主要是为了分割不同组的定时器等,让他们相互之间不受影响
    (6)系统默认注册了5个mode
      a.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
      b.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
      c.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
      d.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
      e.kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
    • Runloop 其它三种模式

    → UITrackingRunLoopMode : (优先切换!!)这个模式就是当UI事件交互的时候Runloop切换到的模式!!!

    场景:这一模式优先级最高,当UI事件交互的时候,都会优先切换到这一模式。

    测试代码如下:

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 repeats:YES block:^(NSTimer * _Nonnull timer) {
      NSLog(@"current Runloop = %@", [NSRunLoop currentRunLoop].currentMode);
    }];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

    结果:

    current Runloop = kCFRunLoopDefaultMode //执行时默认此默认模式
    current Runloop = UITrackingRunLoopMode //当滑动页面的UITextView的时,优先自动切换到此模式。
    current Runloop = kCFRunLoopDefaultMode // 当不对页面的UITextView的操作时,重新恢复到默认模式。


    → NSDefaultRunLoopMode :Runloop的默认模式!只要有事件就处理!

    场景:默认模式,只要有事件就会自动切换到此模式。

    测试代码如下:

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 repeats:YES block:^(NSTimer * _Nonnull timer) {
      NSLog(@"current Runloop = %@", [NSRunLoop currentRunLoop].currentMode);
    }];

    结果:

    current Runloop = kCFRunLoopDefaultMode //程序启动后默认进入此模式

    → NSRunLoopCommonModes :占位符!!(在默认下和UITrackingRunLoopMode下!)

    场景:这个主要用在添加一个NSTimer到RunLoop中。是一个tag,本质上不是一个Mode,默认NSDefaultRunLoopMode和 NSTrackingRunLoopMode都绑定这个tag。

    实例代码:

    static int i = 0;
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
      lable.text = [NSString stringWithFormat:@"始终当前显示的索引:%zi", ++i];
    }];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

    特殊说明:
    1、一个线程对象就对应一个RunLoop对象。创建后,默认启动该MainRunLoop对象。其内部是一个do-while循环。
    2、主线程默认会启动一个Runloop,子线程不会自动启动Runloop需要我们手动启动。见以下代码说明。

    static int i = 0;
    dispatch_queue_t queue = dispatch_queue_create(0, 0);
    //开启一个子线程
    dispatch_async(queue, ^{
    NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
      NSLog(@"display result = %zi, %@", ++i, [NSRunLoop currentRunLoop].currentMode);
    }];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] run]; //手动启动Runloop
    });
    • 图解说明

    • Runloop 总结

      可以看出,RunLoop被开启的线程会一直存在。因为在没有事件发生的时候处于休眠状态,有事件发生的时候处于工作状态。以此来节约CPU资源。这样就可以让一个线程成为常驻线程,也就是说该线程一直存在。

      RunLoop是iOS事件响应与任务处理最核心的机制,它贯穿iOS整个系统。

      RunLoop是一种事件运行循环机制,是保持应用程序持续运行的一种机制。正是由于该机制的存在,应用程序才能在没有事件发生的时候处于休眠状态,有事件发生的时候处于工作状态。以此来节约CPU资源。这也是它的一大特点。

      NSRunLoop是Cocoa框架中的类,与之对应的,在Core Foundation中是CFRunLoopRef类。两者的区别是前者不是线程安全的,后者是线程安全的,且两者可以相互转化。

      RunLoop和线程的关系:
      RunLoop是用来管理线程的,每个线程对应一个RunLoop对象。我们不可以去创建当前线程的RunLoop对象,但是我们可以去获取当前线程的RunLoop。RunLoop就是来监听该线程有无事件发生,如果有就工作,如果没有就休眠。

      主线程的RunLoop对象默认开启,其他线程默认不开启。

      RunLoop与AutoreleasePool;

      RunLoop处理的事件类型;

      RunLoop的运行模式mode;

  • 相关阅读:
    Docker下安装redis
    Goodnotes5
    Notability
    浏览器好用的技术
    苹果平板上好用的软件推荐
    苹果平板爱思助手检验安兔兔
    积分超过排名的第一天
    卸载Windows控制面板的程序和功能中找不到的一些软件的方法
    怎样在GitHub上新建一个文件夹
    Vim的常用操作
  • 原文地址:https://www.cnblogs.com/xujinzhong/p/8436071.html
Copyright © 2020-2023  润新知