一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

iOS开发如何使用Runloop无限循环

时间:2015-11-27 编辑:简简单单 来源:一聚教程网

一. Runloop的基本知识

1. 概念

   runloop是运动循环,不断跑圈,无限循环

   作用:

    保持程序的持续运行 (iOS程序一直活着的原因)

    处理App中的各种事件(eg:触摸事件/定时器事件/selector事件【选择器・performSelector・・・】)

    节省CPU资源,提高程序的性能(有事做事,没事休息)

程序已启动,就开启了一个runloop无限循环,因此程序才能持续的运行

    #import
    #import "AppDelegate.h"
    int main(int argc, char * argv[]) {
     @autoreleasepool {
     return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
     }
    }


2. Runloop对象

1> 在iOS开发中有两套api来访问Runloop

    Foundation框架【NSRunloop】(OC)

    Core Foundation框架【CFRunloopRef】(C)

2> NSRunLoop和CFRunLoopRef都代表着RunLoop对象,它们是等价的,可以互相转换

3> NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)

  

3. Runloop与线程的关系

1> Runloop和线程的关系:一个Runloop对应着一条唯一的线程

    问题:如何让子线程不死

    回答:给这条子线程开启一个Runloop

2> Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建

3> Runloop的生命周期:在第一次获取时创建,在线程结束时销毁


二. Runloop对象的创建

1. Foundation框架【NSRunloop】创建Runloop对象

    // 获取当前线程下的Runloop, 懒加载的形式创建
     NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
    // 获取主线程下的Runloop
    NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];

2. Core Foundation框架【CFRunloopRef】创建Runloop对象

    // 获取当前线程下的Runloop, 懒加载的形式创建
    CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
    // 获取主线程下的Runloop
    CFRunLoopRef runloop2 = CFRunLoopGetMain();


3. Runloop运行原理图

4. Runloop相关的类

    CFRunloopRef

    CFRunloopModeRef【Runloop的运行模式】

    CFRunloopSourceRef【Runloop要处理的事件源】

    CFRunloopTimerRef【Timer事件】

    CFRunloopObserverRef【Runloop的观察者(监听者)】

    Runloop要想跑起来,它的内部必须要有一个mode,mode中必须有source/observer/time,至少要有其中的一个


5. CFRunloopModeRef【Runloop的运行模式】

    Runloop每次启动的时候只能指定一个运行模型,切换模式时必须退出当前的Runloop,再重新进入一个mode,是为了分割不同组的定时器互不影响

    kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行

    UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

    UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

    GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

    kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode

6. CFRunloopTimerRef【Timer事件】

1> NSTimer

    - (void)timer0
    {
     NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
     
     /*
         NSDefaultRunLoopMode -> kCFRunLoopDefaultMode 默认模式主线程中执行
     
         NSRunLoopCommonModes -> kCFRunLoopDefaultMode/UITrackingRunLoopMode
     
         UITrackingRunLoopMode -> 界面追踪,其他不工作
         */
     
     [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    }

2. GCD定时器

GCD定时器必须必须保存起来才能使用

    - (void)gcdTimer
    {
     NSLog(@"+++++++++");
     // 1.创建定时器
     dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
     
     // 2.设置定时器
     /*
         第一个参数:定时器
         第二个参数:从哪个时间开始
         第三个参数:间隔时间
         第四个参数:精确度, 0代表无误差
         */
        dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
     
     // 3.定时器触发事件
        dispatch_source_set_event_handler(timer, ^{
     NSLog(@"----------");
     });
     
     // 4.开启定时器
        dispatch_resume(timer);
     
     // GCD定时器创建是个局部变量需要保存才能执行
        self.timer = timer;
    }


7. CFRunloopSourceRef【Runloop要处理的事件源】

(1)以前的分法

Port-Based Sources

Custom Input Sources

Cocoa Perform Selector Sources


(2)现在的分法

Source0:非基于Port的 (用户触发的时间)

Source1:基于Port的 (通过内核和其它线程相互发送消息)


8. CFRunloopObserverRef【Runloop的观察者(监听者)】

    - (void)observer
    {
     // 创建一个观察者
     /*
         第一个参数:获取内存的方式
         第二个参数:监听哪些状态
         第三个参数:是否持续监听
         第四个参数:0
         第五个参数:block代码块
     
         kCFRunLoopEntry = (1UL << 0),          准备进入
         kCFRunLoopBeforeTimers = (1UL << 1),   即将处理timer事件
         kCFRunLoopBeforeSources = (1UL << 2),  即将处理source事件
         kCFRunLoopBeforeWaiting = (1UL << 5),  即将进入休眠
         kCFRunLoopAfterWaiting = (1UL << 6),   唤醒
         kCFRunLoopExit = (1UL << 7),           退出
         kCFRunLoopAllActivities = 0x0FFFFFFFU  所有
         */
     CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
     
     switch (activity) {
     case kCFRunLoopEntry:
     NSLog(@"准备进入");
     break;
     case kCFRunLoopBeforeTimers:
     NSLog(@"即将处理timer事件");
     break;
     
     case kCFRunLoopBeforeSources:
     NSLog(@"即将处理source事件");
     break;
     
     case kCFRunLoopBeforeWaiting:
     NSLog(@"即将进入休眠");
     break;
     
     case kCFRunLoopAfterWaiting:
     NSLog(@"唤醒");
     break;
     case kCFRunLoopExit:
     NSLog(@"退出");
     break;
     }
     });
     
     // 给runloop添加一个观察者
     CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
     
     NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
     
     
     // 释放观察者
     CFRelease(observer);
    }


9. Runloop运行逻辑




iOS中的Runloop

Runloop是事件接收和分发机制的一个实现。

    Runloop提供了一种异步执行代码的机制,不能并行执行任务。

    在主队列中,Main RunLoop直接配合任务的执行,负责处理UI事件、定时器以及其他内核相关事件。

RunLoop的主要目的:

    保证程序执行的线程不会被系统终止。   

什么时候使用Runloop ?

    当需要和该线程进行交互的时候才会使用Runloop.

    每一个线程都有其对应的RunLoop,但是默认非主线程的RunLoop是没有运行的,需要为RunLoop添加至少一个事件源,然后去run它。

    一般情况下我们是没有必要去启用线程的RunLoop的,除非你在一个单独的线程中需要长久的检测某个事件。

    主线程默认有Runloop。当自己启动一个线程,如果只是用于处理单一的事件,则该线程在执行完之后就退出了。所以当我们需要让该线程监听某项事务时,就得让线程一直不退出,runloop就是这么一个循环,没有事件的时候,一直卡着,有事件来临了,执行其对应的函数。

    Runloop,正如其名所示,是线程进入和被线程用来响应事件以及调用事件处理函数的地方。需要在代码中使用控制语句实现run loop的循环,也就是说,需要代码提供while 或者 for循环来驱动run loop。

    在这个循环中,使用一个Runloop对象[NSRunloop currentRunloop]执行接收消息,调用对应的处理函数。

    Runloop接收两种源事件:input sources和timer sources。

    input sources 传递异步事件,通常是来自其他线程和不同的程序中的消息;

    timer sources(定时器) 传递同步事件(重复执行或者在特定时间上触发)。

    除了处理input sources,Runloop 也会产生一些关于本身行为的notificaiton。注册成为Runloop的observer,可以接收到这些notification,做一些额外的处理。(使用CoreFoundation来成为runloop的observer)。

Runloop工作的特点:

    1> 当有事件发生时,Runloop会根据具体的事件类型通知应用程序作出响应;

    2> 当没有事件发生时,Runloop会进入休眠状态,从而达到省电的目的;

    3> 当事件再次发生时,Runloop会被重新唤醒,处理事件。

提示:一般在开发中很少会主动创建Runloop,而通常会把事件添加到Runloop中。

热门栏目