iOS 开发之 RunLoop 详解

        1)什么是 Runloop ?

            1、字面上是运行循环,内部就是 do-while 循环,在这个循环内不断地处理各种任务

            2、一个线程对应一个 Runloop ,主线程的 RunLoop 默认是开启的。子线程的 RunLoop 需手动开启。

            3RunLoop 只能对应选择一个 Model 启动,如果当期的 Model 中没有任何 Source(Sources0,Sources1)Timmer,那么就直接退出 RunLoop

            4、基本作用就是保持程序的持续运行,处理 APP 中的各种事件、通过 RunLoop ,有事执行,没事休眠,可以节省 CPU 资源,提高程序性能。

 

        2iOS 中有2 API 访问和使用 RunLoop

            Fundation 框架下:NSRunLoop

            Core Fundation 下:CFRunLoopRef

        比较:

            1NSRunLoop CFRunLoopRef 都代表 RunLoop 对象

            2NSRunLoop 是基于 CFRunLoopRef 的一层 OC 封装

 

        3RunLoop 线程:

            1、每条线程都有唯一对应的 RunLoop 对象;

            2、主线程的 RunLoop 已经自动创建好了,子线程的 RunLoop 需要主动创建

            3RunLoop 在第一次获取时创建,在线程结束时销毁。

 

        4)获取 RunLoop 对象:

            Fundation 框架:

                [NSRunLoop currentRunLoop];//获取当前线程的 RunLoop 对象

                [NSRunLoop mainRunLoop];//获取主线程的 RunLoop 对象

            Core Fundation 框架:

                CFRunLoopGetCurrent();  // 获取当前线程的 RunLoop 对象

                CFRunLoopGetMain(); //获取主线程的 RunLoop 对象

 

        5RunLoop 处理逻辑:

            1、通知 Observer:即将进入 Loop

            2、通知 Observer:即将处理 Timer

            3、通知 Observer:将要处理 Source0

            4、处理 Source0

            5、如果有 Source0,跳到步骤 9

            6、通知 Observer:线程即将休眠

            7、休眠,等待唤醒:

                    1> Source0(port)

                    2> timer 启动

                    3> RunLoop 设置的 timer 已经超时

                    4> RunLoop 被外部手动唤醒

            8、通知 Observer:线程将被唤醒

            9、处理未处理的时间:

                    1> 如果用户定义的定时器启动,处理定时器事件并重启 RunLoop。进入步骤 2

                    2> 如果输入源启动,传递相应的消息

                    3> 如果 RunLoop 被显式唤醒而且时间还没超时,重启 RunLoop,进入步骤 2

            10、通知 Observer:即将推出 Loop

 

        6RunLoop 应用场景:

            NSTimer

            ImageView 显示

            PerformSelector

            常驻线程

            自动释放池

 

                应用场景:

                    1> 开启一个常驻线程(让一个子线程不进入消亡状态,等待其他线程发来消息,处理其他事件)

                    2> 在子线程中开启一个定时器

                    3> 在子线程中进行一些长期监控

                    4> 可以控制定时器在特定模式下执行

                    5> 可以让某些事件(行为、任务)在特定模式下执行

                    6> 可以添加 Observer 监听 RunLoop 的状态,比如监听点击事件的处理(在所有点击事件之前做一些事情)

 

        7NSRunLoop 的实现机制,及在多线程中如何使用

            程序创建子线程的时候,才需要手动启动 RunLoop,主线程的 RunLoop 默认开启

            在多线程中,你需要判断是否需要 RunLoop,如果需要,那么你负责配置 RunLoop 并启动,你不需要在任何时候都去启动 RunLoop。例如:使用线程去处理一个预定好的耗时极长的任务时,你就可以无需启动 RunLoopRunLoop 只有在和线程交互时才需要。

 

        8RunLoop Model 的作用:

            mode 主要用来指定事件在运行循环中的优先级的。

            • NSDefaultRunLoopMode kCFRunLoopDefaultMode):默认,空闲状态

            • UITrackingRunLoopMode ScrollView         ScrollView 滑动时会切换到该Mode

            • UIInitializationRunLoopMode run loop       RunLoop 启动时,会切换到该 mode

 

            如果我们把一个 NSTimer 对象以 NSDefaultRunLoopModeKCFRunLoopDefaultMOde 添加到主运行循环中的时候,ScrollView滚动过程中会因为 Mode 的切换,而导致 NSTimer 不在被调度。当我们滚动的时候,也不希望调度,那么就应该使用默认模式。但是,虚妄在滚动时,定时器也要回调,那就应该使用 commonMode

 

            如果想要销毁 Timer ,则必须先将 Timer 置为失效,否则 Timer 就一直占用内存而不会释放。造成逻辑上的内存泄露。该泄露不会使用 Xcode 工具 Instrument 检测出来、另外对于要求必须销毁的 Timer 的逻辑处理,未将 Timer 置为失效,若每次都创建一次,则之前的不能得到释放,则会同时存在多个 Timer 的实例在内存中。

 

            Timer 不需要时,一定要调用 invalidate 方法使定时器失效,否则得不到释放。

 

            如果要 Cell 滚动过程中定时器正常回调,UI 正常刷新,那么要将 Timer 放入 CommonMode 下,因为 NSDefaultRunLoopMode 只有在空闲状态下才会回调。

 

            UIScrollView 的滚动导致 NSTimer 失效:

                1、更改 mode NSRunLoopCommonModes(无论 RunLoop 运行在哪个 Mode都能运行)

                2、切换到主线程来更新 UI 界面刷新

                    // timer添加到 NSDefaultRunLoopMode

                    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];

                    //  然后再添加到 NSRunLoopCommonModes

                    NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];

                    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

 

 

 

 

 

 

 

posted @ 2017-08-31 16:03  ZachRobin  阅读(394)  评论(0编辑  收藏  举报