美文网首页
浅尝iOS中RunLoop

浅尝iOS中RunLoop

作者: 听海听心 | 来源:发表于2017-12-23 12:03 被阅读25次

    今天和大家一起来学习一下RunLoop的基本使用,有疏忽的地方,还望各位不吝赐教。


    一、RunLoop简介

    RunLoop直接翻译过来就是运行循环。

      /*
       * 在UIApplicationMain内部其实启动了一个RunLoop 返回值是一个int类型
       * 所以UIApplicationMain函数一直没有返回,从而保证了程序的运行
       */
       int main(int argc, char * argv[]) {
        @autoreleasepool {
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
          }
      }
       /* 反正都写到这里了,顺带说一句:iOS程序启动做了啥?
         1、执行main函数
         2、执行UIApplicationMain,创建其对象,并设置其代理
         3、开启一个事件循环(主运行循环,死循环:保证应用程序不会退出)
         4、去加载info.plist(判断info.plist当中有没有Main,如果有加载Main.storyboard)
         5、应用程序启动完毕(通知代理应用程序启动完毕)
        */
    

    作用

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

    API

    • Foundation框架
      NSRunLoop:NSRunLoop是对CFRunLoopRef一层封装。
    • coreFoundation框架
      CFRunLoopRef

    二、RunLoop和线程的关系

    1、一一对应的关系 是通过字典的形式进行关联的 线程为Key RunLoop为value来保存的
    2、主线程的RunLoop已经自动创建好了,子线程的需要自行创建
    3、RunLoop在第一次获取时创建,在线程结束时销毁

        // 获得主线程对应的RunLoop
        [NSRunLoop mainRunLoop];
        CFRunLoopGetMain();
        // 获得当前线程的RunLoop
        [NSRunLoop currentRunLoop];
        CFRunLoopGetCurrent()
        // 相互转换
        mainRunLoop.getCFRunLoop;
        // 主线程的RunLoop已经自动创建好了,子线程的需要自行创建
        [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
        // run方法的实现
          - (void)run{
              // 创建子线程对应的RunLoop使用currentRunLoop方法来创建,懒加载
              NSLog(@"NSRunLoop-------------------%@",[NSRunLoop currentRunLoop]);
              NSLog(@"NSThread-------------------%@",[NSThread currentThread]);
          }
    

    三、RunLoop相关类和运行模式

    CoreFoundation中关于RunLoop的五个类

    CFRunLoopRef
    CFRunLoopModeRef
    CFRunLoopSourceRef
    CFRunLoopTimerRef
    CFRunLoopObserverRef

    系统的5个Mode

    kCFRunLoopDefaultMode:App默认的Mode,通常主线程在这个Mode下运行
    UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他mode的影响
    UIInitializationRunLoopMode:用不到,程序刚启动进入的的第一个Mode,启动后就不再使用
    GSEventReceiveRunLoopMode:接收系统事件的内部Mode,用不到
    kCFRunLoopCommonModes:这是一个占位的Mode,不是一种真正的Mode

    • RunLoop中有多个运行模式,但是只能选择一种模式运行
    • 每次RunLoop启动时,只能指定其中的一个Mode,这个Mode被称作CurrentMode
    • 如果要切换Mode,只能退出RunLoop,再重新指定Mode进入,这样做是为了分隔开不同组的Source/timer/Observer,互不影响
    • Mode中至少包含一个timer或者是Resource

    四、RunLoop--CFRunLoopTimerRef

    1、创建定时器方式一

        // 1、创建定时器
        NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
        // 2、添加定时器到runloop,指定runloop为默认模式
        /*
         * 第一个参数:定时器
         * 第二个参数:RunLoop的运行模式
           * NSDefaultRunLoopMode 如果使用本模式,当界面中有拖拽操作的时候,定时器不会工作,因为RunLoop切换了运行模式为 UITrackingRunLoopMode 界面追踪模式
           * UITrackingRunLoopMode 如果使用本模式,当界面中有拖拽操作的时候,定时器才会工作。
           * 如果想两种模式同时使用就都添加或者使用NSRunLoopCommonModes
           * NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode
           * 相当于把timer添加到NSDefaultRunLoopMode模式下,又添加到UITrackingRunLoopMode模式下面
           * NSRunLoopCommonModes并不是真正意义上的模式,用来占位用的,本身是个标签 凡是添加到NSRunLoopCommonModes中的事件同时会被添加到打上Common标签的运行模式上
         */
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        // 3、实现run方法
        - (void)run{
    
            NSLog(@"run-------%@--%@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode);
        }
    

    2、创建定时器方式二

        // 此方法会自动添加RunLoop中,并且设置运行模式为默认
        [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    

    3、在子线程调用定时器

        // 利用NSThread开子线程
        [NSThread detachNewThreadSelector:@selector(timer) toTarget:self withObject:nil];
        // 注意子线程中的RunLoop要手动创建 这里是timer方法的实现
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        // 给方法会自动添加RunLoop中,并且设置运行模式为默认
        [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
        // 开启RunLoop
        [runloop run];
    

    五、RunLoop--CFRunLoopSourceRef

    /*
     * CFRunLoopSourceRef是事件源(输入源)
     * 以前的分类
     * Port——base Source
     * Custom Input Source
     * Cocoa Perform Selector Source
     *
     * 现在的分类 函数调用栈
     * Source0:非基于Port的 用户自定义的
     * Source1:基于Port的 系统的
     */
    

    六、RunLoop--CFRunLoopObserverRef

    /*
     * CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
     * 可以监听的时间点有以下几个
     * kCFRunLoopEntry // 即将进入RunLoop
     * kCFRunLoopBeforeTimer // 即将处理Timer 
     * kCFRunLoopBeforeSources // 即将处理Sources
     * kCFRunLoopBeforeWaiting // 即将休眠
     * kCFRunLoopAfterWaiting // 即将唤醒
     * kCFRunLoopExit // 即将退出
     */
         /*
         * 第一个参数:怎么分配存储空间 kCFAllocatorDefault 默认方式分配
         * 第二个参数:要监听的状态 kCFRunLoopAllActivities 所有的状态
         * 第三个参数:是否持续监听 yes表示持续监听
         * 第四个参数:优先级 总是传递0
         * 第五个参数:当状态改变时候的回调
         */
        CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            /*
            typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
                kCFRunLoopEntry = (1UL << 0), 即将进入RunLoop
                kCFRunLoopBeforeTimers = (1UL << 1), 处理Timer事件
                kCFRunLoopBeforeSources = (1UL << 2), 处理Source事件
                kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入睡眠
                kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒
                kCFRunLoopExit = (1UL << 7), RunLoop退出
                kCFRunLoopAllActivities = 0x0FFFFFFFU 所有状态
            };
             */
            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(@"RunLoop退出");
                    break;
                    
                default:
                    break;
            }
    });
        
        /*
         * 第一个参数:要监听哪个RunLoop
         * 第二个参数:观察者
         * 第三个参数:运行模式
         */
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    //    NSRunLoopCommonModes == kCFRunLoopCommonModes
    //     NSDefaultRunLoopMode == kCFRunLoopDefaultMode
    

    七、RunLoop应用

    /*
     * RunLoop的应用(当然关于RunLoop的应用当然不止这些,笔者也会多多学习,在以后的文章中分享)
     * NSTimer(上面进行了尝试)
     * 常驻线程(下面展示)
     * 自动释放池(这个就算了,笔者认知也有限,福利一下面试)
         自动释放池什么时候释放?
         * RunLoop第一次创建:即将启动的时候创建
         * RunLoop最后一次销毁:即将退出的时候
         * 其他时间创建和销毁:当RunLoop即将睡眠的时候销毁之前的释放池,重新创建一个新的自动释放池
     */
     /** 线程属性 我在界面中添加了两个按钮,一个用于创建线程,一个 用于继续使用当前线程继续执行任务*/
    @property (nonatomic, strong) NSThread *thread;
     /** 创建线程按钮执行的方法 */
      self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(task1) object:nil];
      [self.thread start];
    /** task1任务实现 */
    - (void)task1{
        // 同一个线程中任务是串行执行的
        NSLog(@"执行task1");
    
        // 解决方法,开一个RunLoop,如果没有这个RunLoop self.thread会进入死亡状态,无法再继续执行任务2
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        
        // 如果没有timer或者source 添加保证不让RunLoop退出
        [runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
        
        // 开启
        //    [runloop run];
        // 仅限于子线程的RunLoop,主线程没有效果
        [runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
    }
     /** 继续执行任务按钮方法 */
      // 直接调动会崩溃,因为虽然self.thread被强指针指向,但是当任务1执行完毕后,self.thread已经进入死亡状态,无法再继续执行任务2
       [self performSelector:@selector(task2) onThread:self.thread withObject:nil waitUntilDone:YES];
     /** task2任务实现 */
      - (void)task2{
    
          NSLog(@"执行task2");
      }
    

    写在最后的话:关于iOS-RunLoop的知识今天就学习到这里,关于iOS-RunLoop方面的问题欢迎大家和我交流,共同进步,谢谢各位。

    相关文章

      网友评论

          本文标题:浅尝iOS中RunLoop

          本文链接:https://www.haomeiwen.com/subject/xlukwxtx.html