zoukankan      html  css  js  c++  java
  • RunLoop--

    RunLoop

    • NSRunLoop是IOS消息机制的处理模式
    • 主要作用
      • 一条线程对应一个RunLoop,主线程的RunLoop默认已经创建好了, 而子线程的需要我们自己手动创建
        • 获取主线程对应的RunLoop对象mainRunLoop/CFRunLoopGetMain
        • 获取当前线程对应的RunLoop对象currentRunLoop/CFRunLoopGetCurrent
      • RunLoop会一直循环检测,从线程start到线程end,检测检测到事件源(CFRunLoopSourceRef)执行处理函数,首先会 产生通知,corefunction向线程添加runloopObservers来监听事件,并控制NSRunLoop里面线程的执行和休眠,在有事情做 的时候使当前NSRunLoop控制的线程工作,没有事情做让当前NSRunLoop的控制的线程休眠。
    • RunLoop的运行模式(CFRunLoopModeRef)
      • 一个 RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer
      • 每次RunLoop启动时,只能指定其中一个Mode,这个Mode被称作 CurrentMode
      • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
      • 系统默认模式
        • NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行
        • UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
        • UIInitializationRunLoopMode:在刚启动App时第进入的第一个 Mode,启动完成后就不再使用
        • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
        • NSRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode
      • RunLoop结构图

        RunLoop结构图

    CFRunLoopTimerRef-基于时间的触发器

    CFRunLoopTimerRef基本上说的就是NSTimer

    • 它受RunLoop的Mode影响
      // 新建NSTimer对象
      NSTimer *timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
      // 将NSTimer添加到RunLoop中,并且告诉系统,当前Tiemr只有在RunLoop的默认模式下才有效
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
      // 所以在UITrackingRunLoopMode模式下,定时器的方法不会执行,但定时器仍计时

    NSTimer
    • 将NSTimer添加到RunLoop中,并且告诉系统,当前Tiemr只有在Tracking的默认模式下才有效
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    • 将NSTimer添加到RunLoop中,并且告诉系统,在所有被"标记"common的模式都可以运 行,UITrackingRunLoopMode和kCFRunLoopDefaultMode都被标记为了common模式,所以只需要将timer的 模式设置为forMode:NSRunLoopCommonModes,就可以在默认模式和追踪模式都能够运行

      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    • 如果是通过scheduledTimerWithTimeInterval创建的NSTimer, 默认就会添加到RunLoop得DefaultMode中 , 所以它会自动运行

      NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
      // 虽然默认已经添加到DefaultMode中,但是我们也可以自己修改它的模式
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

    GCD定时器

    • 不受RunLoop的Mode影响
        // 1.创建一个定时器
        // 获取一个全局并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        // 第四个参数:传递一个队列,该队列对应了将来的回调方法在哪个线程中执行
        dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
        // 2.指定定时器开始的时间和间隔的时间, 以及精准度
        // 开始时间
        dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC);
    
        // 间隔时间
        uint64_t interval = 1.0 * NSEC_PER_SEC;
        // 设置定时器
        /*
         第1个参数: 需要给哪个定时器设置
         第2个参数: 定时器开始的时间/DISPATCH_TIME_NOW立即执行
         第3个参数: 定时器开始之后的间隔时间
         第4个参数: 定时器间隔执行的精准度, 传入0代表最精准(尽量的让定时器精准), 传入一个大于0的值, 代表多少秒的范围是可以接受的
         第四个参数存在的意义: 主要是为了提高程序的性能
         注意点: Dispatch的定时器接收的时间是纳秒
         */
        dispatch_source_set_timer(timer, startTime, interval, 0 * NSEC_PER_SEC);
    
        // 3.指定定时器的回调方法
        /*
         第1个参数: 需要给哪个定时器设置
         第2个参数: 需要回调的block
         */
        dispatch_source_set_event_handler(timer, ^{
            NSLog(@"++++++++++++++ %@", [NSThread currentThread]);
    
        });
    
        // 4.开启定时器
        dispatch_resume(timer);

    GCD定时器

    CFRunLoopObserverRef

    • 添加Observer
    *******viewDidLoad********
        // 创建Observer
        /*
         第1个参数: 指定如何给observer分配存储空间
         第2个参数: 需要监听的状态类型/ kCFRunLoopAllActivities监听所有状态
         第3个参数: 是否每次都需要监听
         第4个参数: 优先级
         第5个参数: 监听到状态改变之后的回调
         */
        CFRunLoopObserverRef  observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            switch (activity) {
                case kCFRunLoopEntry:
                    NSLog(@"即将进入runloop");
                    break;
                case kCFRunLoopBeforeTimers:
                    NSLog(@"即将处理timer");
                    break;
                case kCFRunLoopBeforeSources:
                    NSLog(@"即将处理source");
                    break;
                case kCFRunLoopBeforeWaiting:
                    NSLog(@"即将进入睡眠");
                    break;
                case kCFRunLoopAfterWaiting:
                    NSLog(@"刚从睡眠中唤醒");
                    break;
                case kCFRunLoopExit:
                    NSLog(@"即将退出");
                    break;
                default:
                    break;
            }
        });
        // 给主线程的RunLoop添加一个观察者
        /*
         第1个参数: 需要给哪个RunLoop添加观察者
         第2个参数: 需要添加的Observer对象
         第3个参数: 在哪种模式下可以可以监听
         */
        CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
    
        // 释放对象
        CFRelease(observer);
        [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
    
    ********show********
    - (void)show{
        NSLog(@"%s",__func__);
    }

    ![Observer状态变化 . . .]

    • CF的内存管理(Core Foundation)
      • 在Core Foundation框架中,凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release

    RunLoop运行逻辑

    • 逻辑


      苹果官方

      网友整理
      • 1、一个线程对应一个RunLoop,程序一启动,就会默认给主线程创建一个RunLoop,并给这个RunLoop添加一些默认的模式,每个模式有一些timer、source、observer;
      • 2、然后observer就可以监听当前线程的RunLoop,进入RunLoop后就开始处理事件,
      • 3、先处理Timer,再处理source0,然后source1,当把这些事件反复的处理完后,如果没有事件,runLoop就会进入休眠
      • 4、当用户又触发一些新事件,就会唤醒runLoop,回到3,重新处理新的Timer、source0、source1,处理完又回到休眠状态,一直反复操作
      • 5、当程序关闭或强制关闭RunLoop时,observer就会监听到RunLoop退出,流程结束
    • observer

      • 主要为监听RunLoop状态
    • timer
      • 即CFRunLoopTimerRef,也就是NSTimer,当创建一个timer时,必须把它添加到runLoop,它才会执行,在添加时还必须指定模式,决定timer在哪种情况下执行;
      • 常见模式有
        • kCFRunLoopDefaultMode,程序一启动,默认为此模式,当用户不对程序做任何操作时,RunLoop在此模式下运行,当用户开始操作,就会切换为相应模式
        • 因为RunLoop同一时刻只能执行一种模式,就会关闭上一种模式,重新打开一种模式,则此时timer无效,timer只能在设定的模式中有效
        • kCFRunLoopTrackingMode是界面跟踪模式,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响
        • kCFRunLoopCommonMode不是一种真正的模式,是一种标记,系统把kCFRunLoopDefaultMode和 kCFRunLoopTrackingMode标记为kCFRunLoopCommonMode,所以指定kCFRunLoopCommonMode,相 当于既添加到kCFRunLoopDefaultMode,又添加到kCFRunLoopTrackingMode
    • source
      • 一般情况下不会进行操作,官方文档上Source分为三种,分别为基于端口、基于自定义、基于perform selector,但是也可以通过函数调用栈对source进行分类,分为source0和source1,source0是非基于端口的,也就是用户触 发的事件,,source1是系统内部的端口触发的一些事件

    RunLoop应用

    • NSTimer
      • 需指定模式,若GCD则不用
    • ImageView显示
      • 在特定模式下执行某些操作,图片设置与拖拽分别在不同模式
    • PerformSelector
    • 常驻线程
      • 某些操作,需要重复开辟子线程,重复开辟内存过于消耗性能,可以设定子线程常驻
    • 自动释放池
      • 创建和释放
        1.第一次创建, 是在runloop进入的时候创建,对应的状态 = kCFRunLoopEntry
        2.最后一次释放, 是在runloop退出的时候 对应的状态 = kCFRunLoopExit
        3.其它创建和释放
        • 每次睡觉的时候都会释放前自动释放池,然后再创建一个新的
    • 可以添加Observer监听RunLoop的状态
      • 比如监听点击事件的处理(在所有点击事件之前做一些事情)

    注意点

    • 线程只能执行一个操作
      ```objc
    • (void)viewDidLoad
      {
      XMGThread *thread = [[XMGThread alloc] initWithTarget:self selector:@selector(show) object:nil];
      self.thread = thread;
      [thread start];
      }

    • (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event
      {
      // 默认情况下一个线程只能使用一次, 也就是说只能执行一个操作, 执行完毕之后就不能使用了
      [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
      }
      // test方法不会执行
      ```

    • 子线程RunLoop常驻

        // 1.子线程的NSRunLoop需要手动创建
        // 2.子线程的NSRunLoop需要手动开启
        // 3.如果子线程的NSRunLoop没有设置source or timer, 那么子线程的NSRunLoop会立刻关闭
        // 无含义,设置子线程为常住线程,让子线程不关闭
        // [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
      
        NSTimer *timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
        // 会添加到当前子线程
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] run];
      • 注意
        • NSRunLoop只会检查有没有source和timer, 没有就关闭, 不会检查observer
        • 主线程没有到期时间,子线程有
          • 转载/

  • 相关阅读:
    千万级规模高性能、高并发的网络架构经验分享
    CPU高问题排查
    Worker+MQ解惑
    HashMap解惑
    配置时间同步时,遇到同步无法成功的解决方法
    Django基础—— 1.WEB框架介绍
    html5兼容性问题
    jQuery基础——节点操作
    jQuery基础——基本操作
    jQuery基础——选择器
  • 原文地址:https://www.cnblogs.com/cdp-snail/p/4907555.html
Copyright © 2011-2022 走看看