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
        • 主线程没有到期时间,子线程有
          • 转载/

  • 相关阅读:
    httpcontext in asp.net unit test
    initialize or clean up your unittest within .net unit test
    Load a script file in sencha, supports both asynchronous and synchronous approaches
    classes system in sencha touch
    ASP.NET MVC got 405 error on HTTP DELETE request
    how to run demo city bars using sencha architect
    sencha touch mvc
    sencha touch json store
    sencha touch jsonp
    51Nod 1344:走格子(贪心)
  • 原文地址:https://www.cnblogs.com/cdp-snail/p/4907555.html
Copyright © 2011-2022 走看看