深度研究

RunLoop-基础篇

2016-06-15  本文已影响633人  js丶

前言

2016年6月7号开始编写CFRunLoop,并通过实现代码

NSRunLoop

NSRunLoop使用方法

访问NSRunLoop属性和方法:

currentMode
+ currentRunLoop
- limitDateForMode:
+ mainRunLoop
- getCFRunLoop

管理计时器:
- addTimer:forMode:
管理端口
- addPort:forMode
- removePort:forMode:
运行一个循环
- (void)run

Discussion:

- runMode:beforeDate:在指定模式下运行该循环,直到给定日期为止,返回True 运行循环运行和处理输入源,如果是False,如果指定的超时值达到时,Run Loop将无法启动

Discussion:

终止循环
Example:

在需要终止的地方调用shouldKeepRunning为NO

BOOL shouldKeepRunning = YES;        // global
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

限制RunLoop终止时间
- (BOOL)runMode(NSString *)mode beforeDate:(NSDate *)limitDate

Discussion:

RunLoop调度和取消消息

// 此方法设置一个定时器,在下次运行循环开始执行aselector消息接收器,指定Mode参数设置定时器在该模式中运行
- performSelector:target:argument:order:modes:

Discussion

// 使用此方法取消的消息先前预定的使用的performSelector:target:argument:order:modes:,方法从运行循环的所有模式中移除执行请求。
- cancelPerformSelector:target:argument:
// 此方法取消与目标关联的原定信息,忽略了对调度操作的选择和参数。
- cancelPerformSelectorsWithTarget:

Run Loop Mode
NSRunLoop定义以下RunLoopMode运行循环模式。
处理除NSConnection以外的对象的输入源模式。

extern NSString* const NSDefaultRunLoopMode;
extern NSString* const NSRunLoopCommonModes;

CFRunLoop

CFRunLoop对象监控输入任务的来源和分派已处理的任务对它进行控制,输入源包括user input device(用户输入设备)、network connections(网络连接), periodic or time-delayed events(周期或时滞事件), and asynchronous callbacks(异步回调).

提供三种类型的对象监视Run Loop

处理这些对象时接收回调需要处理,首先将这些对象放到一个 RunLoop。

(1)每个源、计时器和观察者添加到Run Loop必须指定一个或多个运行循环模式(Run Loop Mode)相关联。

(2)模式确定后,在给定的迭代过程中RunLoop处理哪些事件。每次运行循环执行时,它会在特定模式下运行。在这种模式下,运行循环过程只处理与源、计时器、观察者与该模式相关联的事件。

(3)默认运行循环方式由kCFRunLoopDefaultMode常数指定,当应用程序(或线程)处于闲置状态时处理事件。

(4)RunLoop定义了其他的模式,并且可以在其他模式中执行运行循环,以限制源、计时器和观察员被处理(例如以pseudo-mode为核心基础,称为公共模式,允许将多个模式与给定的源、计时器或观察者关联起来),还可以定义自己的自定义模式来限制事件处理,如果指定的是其他模式,可能处理滑动事件(UITableView/UIScrollView)时,限制其他模式(例如网络IO处理)。
(5)添加一个模式的常见模式,使用CFRunLoopAddCommonMode函数。

功能

获得一个运行循环

CFRunLoopGetCurrent:返回当前线程CFRunLoop对象

Discussion:

CFRunLoopGetMain:返回主CFRunLoop对象。

启动和停止循环

CFRunLoopRun:在当前线程的使用默认模式运行

Discossion:

CFRunLoopRunResult CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled );当前线程的CFRunLoop对象运行在一个特定的模式。

Parameters:

Discussion:

返回码为CFRunLoopRunInMode,确定运行循环退出的原因,以下返回值:

kCFRunLoopRunFinished。运行循环模式模式没有源或计时器。

kCFRunLoopRunStopped。运行循环停止调用CFRunLoopStop函数。

kCFRunLoopRunTimedOut。时间间隔过去了(运行RunLoop超时)。

kCFRunLoopRunHandledSource。源已经被处理。这种退出条件,只有在RunLoop运行循环被告知只运行到一个源处理。
CFRunLoopWakeUp:唤醒一个等待CFRunLoop对象
CFRunLoopStop:强制CFRunLoop对象停止运行。
CFRunLoopIsWaiting:返回一个布尔值来指示运行循环等待一个事件,如果是True,rl没有事件要处理和阻塞,等待一个源或计时器处于就绪状态;如果为False,rl要么不运行或正在处理的源程序,计时器,或观察者。

管理观察

CFRunLoopAddObserver 添加一个CFRunLoopObserver对象运行循环模式。

Discussion:

管理运行循环模式

CFRunLoopAddCommonMode:添加一个模式到设定的RunLoop的comomon modes(组合模式),RunLoopMode添加到Common modes模式,它不能被删除。
CFRunLoopCopyAllModes:返回一个数组,其中包含所有CFRunLoop对象定义的模式。
CFStringRef CFRunLoopCopyCurrentMode ( CFRunLoopRef rl ):返回一个给定的运行循环当前正在运行的模式的名称

管理计时器

void CFRunLoopAddTimer ( CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode ):添加一个CFRunLoopTimer对象到运行循环模式。
CFAbsoluteTime CFRunLoopGetNextTimerFireDate ( CFRunLoopRef rl, CFStringRef mode ):返回的时间是下一个就绪计时器
CFRunLoopRemoveTimer ( CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode ):从一个运行循环模式,删除一个CFRunLoopTimer对象。
CFRunLoopContainsTimer ( CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode ):返回一个布尔值,用于显示运行循环模式是否包含一个特定CFRunLoopTimer对象。

调度模块

void CFRunLoopPerformBlock ( CFRunLoopRef rl, CFTypeRef mode, void (^block)(void) ):排入给定RunLoop ,在指定的RunLoopMode下runloop周期执行block任务。

线程与运行循环

RunLoop代码逻辑

根据苹果官方文档对RunLoop代码逻辑是这么描述的

A run loop is very much like its name sounds. It is a loop your thread enters and uses to run event handlers in response to incoming events. Your code provides the control statements used to implement the actual loop portion of the run loop—in other words, your code provides the while
or for
loop that drives the run loop. Within your loop, you use a run loop object to "run” the event-processing code that receives events and calls the installed handlers.

- (void)loop {
    initialize()
    while (message != quit) {
        // 获取唤醒事件
        id  message = get_next_message()
        // 处理事件
        process_message(message)
    }
}

RunLoop的概念结构和各种各样的来源如下图所示。 输入源提供异步事件到相应的处理程序,并造成runUntilDate:方法(称为线程的相关NSRunLoop对象)(runUntilDate可以查阅NSRunLoop方法Discussion介绍)退出。 计时器来源提供事件处理程序,不会引起循环运行退出。

RunLoop及其来源架构

除了处理源的输入外,运行循环也会产生关于循环行为的通知。注册的运行循环的观察员可以接收这些通知,并使用它们在线程中进行附加处理。

Run Loop Observers

当一个适当的异步或同步事件发生时,在特定位置的Run Loop Observers执行循环过程中,可以使用Run Loop Observers给准备线程或准备在休眠之前线程处理一个给定的事件。在运行循环中与下列事件关联来观察Run Loop Observers:

苹果官方提供CFRunLoopActivity枚举来观察RunLoop活动阶段

enum CFRunLoopActivity {
   kCFRunLoopEntry = (1 << 0), // 即将进入运行循环
   kCFRunLoopBeforeTimers = (1 << 1),  // 当运行循环将要处理一个计时器时。
   kCFRunLoopBeforeSources = (1 << 2), // 当运行循环将要处理一个输入源时。
   kCFRunLoopBeforeWaiting = (1 << 5), // 当运行循环将要休眠。
   kCFRunLoopAfterWaiting = (1 << 6), // 当运行循环被唤醒时,在被唤醒之前,处理唤醒事件
   kCFRunLoopExit = (1 << 7),   // 退出运行循环。
   kCFRunLoopAllActivities = 0x0FFFFFFFU  // 结合之前的所有阶段
};
typedef enum CFRunLoopActivity CFRunLoopActivity;

通常使用CFRunLoopObserverCreate、CFRunLoopObserverCreateWithHandler来监听RunLoop状态,接收回调信息(常见于自动释放池创建销毁)

例子:

//
//  ConfiguringRunLoopExample.m
//  RunLoopExample
//
//  Created by lmj  on 16/6/7.
//  Copyright © 2016年 linmingjun. All rights reserved.
//

#import "ConfiguringRunLoopExample.h"

@interface ConfiguringRunLoopExample ()

@end

@implementation ConfiguringRunLoopExample

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    [NSThread detachNewThreadSelector:@selector(threadMain) toTarget:self withObject:nil];
//    [self threadMain];
//    [self performSelectorInBackground:@selector(threadMain) withObject:nil];
//    [self performselector]
//    [self performSelector:@selector(threadMain) withObject:nil afterDelay:0];
    
}

- (void)threadMain
{
    // The application uses garbage collection, so no autorelease pool is needed.
    // 获得当前thread的Run loop
    NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
    
//    typedef void (*myObserver)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
    // Create a run loop observer and attach it to the run loop.
    // 设置Run loop observer的运行环境
    CFRunLoopObserverContext  context = {0, (__bridge void *)(self), NULL, NULL, NULL};
    // 创建Run loop observer对象
    // 第一个参数用于分配observer对象的内存
    // 第二个参数用以设置observer所要关注的事件,详见回调函数myRunLoopObserver中注释
    // 第三个参数用于标识该observer是在第一次进入run loop时执行还是每次进入run loop处理时均执行
    // 第四个参数用于设置该observer的优先级
    // 第五个参数用于设置该observer的回调函数
    // 第六个参数用于设置该observer的运行环境
    CFRunLoopObserverRef    observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                                               kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver,&context);
//    CFRunLoopObserverRef observer2 = CFRunLoopObserverCreateWithHandler
//    (kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
//        //The entrance of the run loop, before entering the event processing loop.
//        //This activity occurs once for each call to CFRunLoopRun and CFRunLoopRunInMode
//    });
    
    
    
    if (observer)
    {
        // 将Cocoa的NSRunLoop类型转换成Core Foundation的CFRunLoopRef类型
        CFRunLoopRef    cfLoop = [myRunLoop getCFRunLoop];
        // 将新建的observer加入到当前thread的run loop
        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);
}

- (void)doFireTimer:(NSTimer *)timer {
    NSLog(@"current thread: %@",[NSThread currentThread]);
    NSLog(@"doFireTimer, %f", timer.timeInterval);
}

void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    switch (activity) {
            //The entrance of the run loop, before entering the event processing loop.
            //This activity occurs once for each call to CFRunLoopRun and CFRunLoopRunInMode
        case kCFRunLoopEntry:
            NSLog(@"run loop entry");
            break;
            //Inside the event processing loop before any timers are processed
        case kCFRunLoopBeforeTimers:
            NSLog(@"run loop before timers");
            break;
            //Inside the event processing loop before any sources are processed
        case kCFRunLoopBeforeSources:
            NSLog(@"run loop before sources");
            break;
            //Inside the event processing loop before the run loop sleeps, waiting for a source or timer to fire.
            //This activity does not occur if CFRunLoopRunInMode is called with a timeout of 0 seconds.
            //It also does not occur in a particular iteration of the event processing loop if a version 0 source fires
        case kCFRunLoopBeforeWaiting:
            NSLog(@"run loop before waiting");
            break;
            //Inside the event processing loop after the run loop wakes up, but before processing the event that woke it up.
            //This activity occurs only if the run loop did in fact go to sleep during the current loop
        case kCFRunLoopAfterWaiting:
            NSLog(@"run loop after waiting");
            break;
            //The exit of the run loop, after exiting the event processing loop.
            //This activity occurs once for each call to CFRunLoopRun and CFRunLoopRunInMode
        case kCFRunLoopExit:
            NSLog(@"run loop exit");
            break;
            /*
             A combination of all the preceding stages
             case kCFRunLoopAllActivities:
             break;
             */
        default:  
            break;  
    }  
}



@end
运行结果

Parameters

** allocator **

activities

repeats

order

**block **

observer

**activity **

RunLoop 内部的逻辑:

每次运行它,线程的RunLoop处理等待事件,并生成通知附加到观察者上。它的顺序具体执行顺序如下图

RunLoop内部执行顺序

Configuring the Run Loop

未完待续

上一篇 下一篇

猜你喜欢

热点阅读