• 多线程篇-RunLoop


    RunLoop

    简述

    1、RunLoop是事件接收和分发机制的一个实现
    2、并且它能处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
    3、以及节省CPU资源,提高程序性能:(该做事时做事,该休息时休息)
    

    如何获取Runloop对象:

    这里的话IOS提供了两套API来访问或使用RunLoop
        1、CFRunLoopRef      是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。
        2、NSRunLoop         是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。
    

    CFRunLoopRef的代码是开源的,你可以在这里CFRunLoopRef源码下载到整个 CoreFoundation 的源码

    每一个线程对应着一个RunLoop,但是线程在创建的时候是没有RunLoop的,如果你不去获取它,它会一直没有,当然必须你自己的主动去获取,但是在你线程结束的时候,你所获取的RunLoop也跟着销毁了。如果你需要在某个线程对你自己的RunLoop执行一些事件的时候,那么你就的在线程未结束之前进行操作,然而在程序中是具有一个主RunLoop的,它用来管理程序的生死,具体的话是在UIApplicationMain里面执行

    //具体显示
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            //程序开始执行  会输出这段语句
            NSLog(@"------------------");
            //可以看出这里面是一直执行的  相当于一个死循环
            int result = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
            //程序结束了才会执行  会输出这段语句,在未结束之前这句话是不会执行的
            NSLog(@"++++++++++++++++++");
            return result;
        }
    }
    
    

    获取的方式

    //获取的两种方式
    
    1、这种为CFRunLoopRef中的
        CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
        CFRunLoopGetMain(); // 获得主线程的RunLoop对象
    
    2、这种为NSRunLoop中的
        [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
        [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
    

    相关类

    //相关的五个类
    
    1、CFRunLoopRef
        1、代表一个RunLoop对象
    2、CFRunLoopModeRef
        1、代表RunLoop的运行模式
            1、一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer
            2、每次RunLoop启动时,只能指定其中一个 Mode,如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
            3、同一时刻只能进行一种模式
        2、苹果内部提供了五种模式
            1、kCFRunLoopDefaultMode (NSDefaultRunLoopMode)
                1、App的默认Mode,通常主线程是在这个Mode下运行
            2、UITrackingRunLoopMode
                1、界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
            //这个通常用不到
            3、UIInitializationRunLoopMode
                1、在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
            //这个通常用不到
            4、GSEventReceiveRunLoopMode
                1、接受系统事件的内部 Mode
            5、kCFRunLoopCommonModes
                1、这是一个占位用的Mode,这个的话用语言很难表达,后面会看到实例中会使用到这里,大家仔细体会
    3、CFRunLoopSourceRef
        1、用来管理所有事件的事件源,包括自定义的事件,以及系统自带的事件。
        2、Source有两个版本:Source0 和 Source1
            1、Source0-----为用户主动触发的事件
            2、Source1-----通过内核和其他线程相互发送消息。
    4、CFRunLoopTimerRef
        1、基本上说的就是NSTimer,基本用法如下实例标示
    5、CFRunLoopObserverRef
        1、用来监听RunLoop的状态改变
        2、状态列表
            kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
            kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
            kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
            kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
            kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
            kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
            kCFRunLoopAllActivities = 0x0FFFFFFFU //所有状态
    

    补充

    一个RunLoop有很多Mode,一个Mode里面有很多得Source/Timer/Observer,但是同一时刻只能进行一种模式。 如图:

    实例

    • CFRunLoopTimerRef

    • 首先在我们的storyboard添加一个text view控件
      然后使用代码

    • 代码

      -(void)viewDidLoad {
          [super viewDidLoad];
          //在原来使用time的时候,我们是直接这样写的,它是直接添加到RunLoop的DefaultMode模式中去得,如果我们去滑动text view的时候,也就是说我们现在操作的是RunLoop的Tracking,因为在前面我们并没有把time添加到Tracking中去,那么滑动的时候是不会输出的,
          //创建time
          NSTimer *time = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
          //在这里我们把time添加到Tracking中去,那么操作就会发现,默认的是时候它也会输出,滑动text view的时候他也会输出了
          [[NSRunLoop currentRunLoop]addTimer:time forMode:UITrackingRunLoopMode];
      }
      -(void)show{
          NSLog(@"%s",__func__);
      }
      

      把time添加到NSDefaultRunLoopMode模式下

      NSTimer *time = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
      //在这里的话可以看到,我们滑动的时候它并没有输出,因为我们forMode的为NSDefaultRunLoopMode,也就是通常主线程的这个Mode下运行
      [[NSRunLoop currentRunLoop] addTimer:time forMode:NSDefaultRunLoopMode];
      

      把time添加到UITrackingRunLoopMode模式下

      NSTimer *time = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
      

    // 在这里的话可以看到,我们滑动的时候它才会输出,因为我们forMode的为UITrackingRunLoopMode,
    [[NSRunLoop currentRunLoop] addTimer:time forMode:UITrackingRunLoopMode];
    看了上面两种是不是有种感觉为什么两者不能共存,那么下面这种就可以看到它们共存了 objc
    NSTimer *time = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
    // 在这里的话可以看到,我们不管是程序启动还是滑动的时候它都会输出,因为我们forMode的为kCFRunLoopCommonModes,
    [[NSRunLoop currentRunLoop] addTimer:time forMode:kCFRunLoopCommonModes];
    ```

    • 补充

      关于定时器的话是有两种的一个是NSTime,但是它是会受RunLoop的模式所影响的,一个是GCD的定时器,它呢是不受RunLoop的模式所影响的,这里的话留给大家一个引子(GCD的定时器是如何不受RunLoop模式的影响),这个也是公司一般很爱问的一个问题。

    • CFRunLoopObserverRef

    • 代码

      - (void)viewDidLoad {
          [super viewDidLoad];
          /*
              第一个参数:指定如何给obsever分配存储空间
              第二个参数:需要监听的类型/kCFRunLoopAllActivities为全部
              第三个参数:是否每次都监听
              第四个参数:优先级
              第五个参数:监听状态改变之后的回调函数
           */
          CFRunLoopObserverRef obsever = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
      //        kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
      //        kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
      //        kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
      //        kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
      //        kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
      //        kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
      //        kCFRunLoopAllActivities = 0x0FFFFFFFU //所有状态
              switch (activity) {
                  case kCFRunLoopEntry:
                      NSLog(@"即将进入Loop");
                      break;
                  case kCFRunLoopBeforeTimers:
                      NSLog(@"即将处理 Timer");
                      break;
                  case kCFRunLoopBeforeSources:
                      NSLog(@"即将处理 Source");
                      break;
                  case kCFRunLoopBeforeWaiting:
                      NSLog(@"即将进入休眠");
                      break;
                  case kCFRunLoopAfterWaiting:
                      NSLog(@"刚从休眠中唤醒");
                      break;
                  case kCFRunLoopExit:
                      NSLog(@"即将退出Loop");
                      break;
                  default:
                      break;
              }
          });
          //给主线程的RunLoop添加一个观察者
          /*
           第一个参数:需要给那个RunLoop添加观察者
           第二个参数:需要添加的observer
           第三个参数:在那种模式下监听
           */
          CFRunLoopAddObserver(CFRunLoopGetMain(), obsever,kCFRunLoopDefaultMode );
          CFRelease(obsever);        
      }
      
    • 补充

      这里的话如果打印出来,是会具有很多time和Source的,因为苹果内部进行了一系列的调用,那么大家可以明显的看到,这里是如何监听RunLoop状态是如何改变的,最后一定要记得去release,因为ARC无法释放Core Foundation 框架中的Create、Copy、Release

                     本章到此结束
               欢迎各位码友随意转载并指正
  • 相关阅读:
    windows任务计划
    PHP创建多级文件夹
    js正则表达式(不断总结中....)
    js随机改变背景颜色
    提高PHP代码质量36计
    PHP中使用Filter进行数据安全过滤
    CSS颜色代码大全
    正则表达式的修饰符
    创建索引视图
    读取xml中的所有文件名
  • 原文地址:https://www.cnblogs.com/ljy-666/p/5135997.html
Copyright © 2020-2023  润新知