Runloop

来源:互联网 发布:淘宝卖的夜磨牙垫好吗 编辑:程序博客网 时间:2024/04/29 03:47

RunLoops

概念

runloop是线程相关的基础对象,runloop是一个被用来处理事件或者timer的循环,runloop的目的是用来让线程在需要的时候运行,并且在不需要的时候休眠。

runloop的控制并不是自动的,你必须要手动管理。cocoa 和 foundation 提供了runloop对象来帮助你管理runloop,因而你不用创建。比如主线程就有自己的runloop,并且是自动运行的。子线程的runloop需要手动运行。需要注意的是,framwork的线程会自动建立并且作加入到主线程中作为应用的启动。

runloop分解

runloop就像他的名字一样是一个不断循环运行的环,runloop可以接受两种数据来源,
1. input source,可能是来源于其他线程的数据 会导致执行runUntilDate 执行
2. timer 不会导致执行runUntilDate 执行

runloop

除了可以处理输入的source,通过注册观察者也可以拿到runloop的通知代理。

runloop mode

mode可以很好的把事件和timer进行分类,这样可以更好地处理和管理不同类型的数据源,

cocoa 和 coreFoundation 可以创建一些默认的或者一些其他常用的mode。当然也可以自定义一个mode,但是所有的mode的runloop都是一样的只是名字不同。

同一时间只能进入一个mode,所以可以优先处理高优先级的mode,等待优先级高的mode结束之后再处理其他mode

几种不同的mode

mode name description default kCFRunLoopDefaultMode 最常用 Modal NSModalPanelRunLoopMode 可用于modal panels EventTracking NSEventTrackingRunLoopMode 可用于检测滑动或者其他用户操作的操作环 CommonModes kCFRunLoopCommonModes mode集合,可以自己配置,默认有default mode,可添加。

input sources

port-based sources

cocoa和corefoundation 中corefondation需要创建port和source 需要使用port对象进行管理。

custom input source

使用 CFRunLoopSourceRef 创建相关的对象和回调方法,那么cf就会自动在指定时间调用这些回调,从而可以配置source
事件到达时需要自定义转发机制,这部分需要提供数据源和转发机制。

Cocoa Perform Selector Sources

使用一些api完成进程通讯,可以减轻很懂工作,并且可以自动移除source
在子线程中必须自己启动runloop,或者你甚至可以在程序一启动就立刻开启一个runloop,这样的话,你就可以一直在这个线程去做数据处理了。

TImer source

timer 的运行必须在runloop相同的mode下,并且需要等待runloop结束当前的handler处理才会执行。
timer的执行并不会按照fire的时间,而是以schedule 的时间为准。

NSMachPort

创建np(NSMachPort)并且添加到runloop中,添加子线程时将np传给子线程,子线程也可以用这个np去传递消息回来。

首先NSMarchPort是一个task内线程之间异步通信用的
你需要在你的两个object内重写handlePortMessage:方法来截取接收到的消息,使用NSPortMessage来发送消息
消息一般都是有唯一id和数据组成的
数据需要经过序列化通过NSPortMessage的components来传递
接收到数据之后需要进行反序列化

runloop 的观察者

可以添加一些runloop的观察者,这样就可以在指定时间观察到runloop的执行步骤,并且在这些步骤中做一些准备工作。你可以检测到的runloop执行
1. 开始
2. timer执行
3. inputsource 执行
4. 休眠前
5. 唤醒前。
6. 退出。
可以通过CFRunLoopObserverRef,使用例程

- (void)threadMain{    // The application uses garbage collection, so no autorelease pool is needed.    NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];    // Create a run loop observer and attach it to the run loop.    CFRunLoopObserverContext  context = {0, self, NULL, NULL, NULL};    CFRunLoopObserverRef    observer = CFRunLoopObserverCreate(kCFAllocatorDefault,            kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);    if (observer)    {        CFRunLoopRef    cfLoop = [myRunLoop getCFRunLoop];        CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);    }    // Create and schedule the timer.    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self                selector:@selector(doFireTimer:) userInfo:nil repeats:YES];    NSInteger    loopCount = 10;    do    {        // Run the run loop 10 times to let the timer fire.        [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];        loopCount--;    }    while (loopCount);}

runloop的执行过程

runloop的观察者虽然可以检测到runloop的执行步骤,但是由于是异步检查,所以时间可能不会非常精准,可以使用awake-from-sleep notification去校准时间。
我们可以使用runloop对象直接唤醒,但是也可以直接给他一个inputsource,runtime也会被唤醒执行任务。

什么时候会使用一个runloop呢?

程序的主线程无需启动,因为会自动创建,runloop也会自动创建。
对于子线程,如果你需要更加自由的配置runloop,那么你可能就需要自己管理了。比如如下的场景你可能就需要使用runloop了

  1. 线程间的通讯,或者自定义输入源
  2. timers
  3. perforselector
  4. 保存一个线程一直运行。

必须恰当的关闭一个线程,从而避免内存泄露

如何使用一个runloop

runloop是一个用来管理线程的对象,可以给线程添加事件来源,timer来源,runloop观察者,一个线程都有一个唯一的runloop,可以用 currentRunLoop 获取。

配置runloop

在runloop开始前,必须给其添加事件源,不然runloop就会直接休眠了。
初次之外,也可以添加观察者到runloop,这样就可以监测runloop的执行,如上一个代码片。
当需要保持一个长执行的runloop的时候,最好添加一个inputsorce来接受和发送信息。虽然一个repeqt timer也可以做到,但是不如inputsource效率高。

开启runloop

可以用以下三种方式开启runloop

  1. 无条件开启
    无条件开启会导致runloop一直运行,而且无法控制

  2. 设置一个时间期限
    在deadline到来之前或者Event到来之前,runloop会一直执行。当有事件的时候,runloop就会把事件分发给处理着,然后退出。然后就可以执行下一个任务了。

  3. 以某个mode开启。
    除了使用timeout也可以使用mode开启runloop

使用的大概方式
例子使用了CFRunloopMode

- (void)skeletonThreadMain{    // Set up an autorelease pool here if not using garbage collection.    BOOL done = NO;    // Add your sources or timers to the run loop and do any other setup.    do    {        // Start the run loop but return after each source is handled.        SInt32    result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);        // If a source explicitly stopped the run loop, or if there are no        // sources or timers, go ahead and exit.        if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished))            done = YES;        // Check for any other exit conditions here and set the        // done variable as needed.    }    while (!done);    // Clean up code here. Be sure to release any allocated autorelease pools.}

结束runloop

有两种方法可以结束runloop

  1. 设定超时
  2. 手动结束

设定超时是比较好的一种方法,可以让runloop尽量回收他的资源,并且完成包括notification等的执行过程。
使用CFRunloopStop会让runloop像timeout一样结束,可以让他在结束之前完成notification等动作再结束。
移除输入源虽然可以让runloop停止,但是这个方法并不可靠,因为有可能有一些inputsource你并不知道。

线程安全和runloop对象

Core Foundation 中的api一般都是线程安全的,并且可以在任何runloop中使用,包括你不控制的runloop。
NSRunloop 中的API最好只用在你管理的runloop里面。

0 0