iOS专题资源__系统知识点面试计算机技术一锅炖

iOS-RunLoop

2016-10-20  本文已影响293人  岁与禾

2016年8月2日

RunLoop

有两种类型的RunLoop,一种是Foundation框架的 NSRunLoop;另一种是Core Foundation核心框架的CFRunLoopRef。前者是OC语言封装的,后者是纯C语言底层的。

基本作用:

1 Main函数中的RunLoop

int main(int argc,char *argv[])
{
    @autoreleasepool{
        
        return UIApplicationMain(argc,argv,nil,NSStringFromClass([AppDelegate class]))
    
    }
}

UIApplicationMain实际上是没有返回值的,因为在内部启动了一个RunLoop循环。

这个默认启动的RunLoop是跟主线程相关联的。

2 RunLoop与线程

3 获取RunLoop对象

(1)Foundation对象

//获取主线程的运行循环
[NSRunLoop mainRunLoop];
//获取当前线程的RunLoop对象(如果没有,就会创建,是懒加载形式)
[NSRunLoop currentRunLoop];

(2)Core Foundation对象

//获取当前线程的运行循环
CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
//获取主线程的运行循环
CFRunLoopRef mainRunLoop = CFRunLoopGetMain();

(3)互相转换

CFRunLoopRef *cfMainRunLoop = [NSRunLoop mainRunLoop].getCFRunLoop;

4 RunLoop运行模式Mode

一个RunLoop可以包含多个Mode,每个RunLoop启动后,只能执行一个Mode。

系统默认注册了五个运行模式(以Core Foundation为例,Foundation跟其一样,改个NS前缀就行)

5 NSTimer

NSTimer定时器,有两种创建的方式:

//1 创建计时器,自动加入到当前运行循环,并启动。默认为DefaultMode
NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doSomething) userInfo:nil repeats:YES];

//2 创建定时器,需要手动加入当前的运行循环
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(doSomething) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

注意:子线程是需要我们手动创建运行循环并启动运行的。如果当前计时器是在子线程中创建,需要获取一下当前运行循环(因为懒加载,会自动创建)。然后并启动。

//当前的线层为子线程
NSLog(@"%@",[NSThread currentThread]);

//创建子线程对应的运行循环--因为是懒加载,如果没有会自动创建
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];

//创建定时器1--自动加入循环并启动
//[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doSomething) userInfo:nil repeats:YES];

//创建定时器2--手动加入循环并启动
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(doSomething) userInfo:nil repeats:YES];
[currentRunLoop addTimer:timer forMode:NSRunLoopCommonModes];

//启动运行循环
[currentRunLoop run];

6 GCD定时器

XCode提供了代码段,输入dispatch_source_timer回车就可以。
有三部分:

代码示例:

//1 创建GCD定时器
/*
 *  @param DISPATCH_SOURCE_TYPE_TIMER CFRunLoopSourceRef类型(定时器也是一种source)
 *  @param 0                          暂时不清楚,默认就是0
 *  @param 0                          暂时不清楚,默认就是0
 *  @param dispatchQueue              定时器任务执行的队列
 */
//---1.1 创建全局队列(任务执行的队列)
dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//---1.2 创建定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatchQueue);
self.timer = timer;//延长生命周期

//2 设置定时器(启动时间、时间间隔、定时器处理的任务)
/**
 *第一个参数:timer,设置哪个定时器
 *第二个参数:DISPATCH_TIME_NOW 启动时间
 *第三个参数:时间间隔(秒),他会自动转换成系统支持的纳秒
 *第三个参数:精确度,0表示绝对精确
 */
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);


//3 设置定时器任务
dispatch_source_set_event_handler(timer, ^{
    
    NSLog(@"定时器执行任务");
    
});


//4 启动定时器(需要注意timer的声明周期,避免提前销毁了,但是任务还没开始执行)
dispatch_resume(timer);

<b style="color:red">
注意:

相比NSTimer,GCD定时器不会受RunLoopMode的影响。精度比NStimer高

</b>

7 CFRunLoopSourceRef

就是一个RunLoop的源,其实timer也是源的一种。除了timer之外,源的分类分两种情况

<b style="color:orange">
之前的分类:
</b>

<b style="color:orange">
之后的分类(可以在函数调用栈中看出来--断点调试的时候能够看到):
</b>

<b style="color:orange">
给RunLoop添加Source
</b>

//1 Foundation的添加源
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];

//2 Core Foundation添加源
CFRunLoopSourceRef source = CFRunLoopSourceCreate(CFAllocatorGetDefault(), 0, nil);
CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);

8 CFRunLoopObserverRef

观察者:也就是RunLoop的观察者,能够监听RunLoop的状态改变。

RunLoop的状态有如下几种:

/**
     *  kCFRunLoopEntry = (1UL << 0),  当RunLoop启动的时候
        kCFRunLoopBeforeTimers = (1UL << 1),  即将处理计时器
        kCFRunLoopBeforeSources = (1UL << 2), 即将处理source
        kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入睡眠状态
        kCFRunLoopAfterWaiting = (1UL << 6),  进入睡眠状态
        kCFRunLoopExit = (1UL << 7), RunLoop退出
        kCFRunLoopAllActivities = 0x0FFFFFFFU 所有的状态
     */

所以我们在增加监听器的时候,一般监听的是所有的状态,activity传入的值为kCFRunLoopAllActivities

示例:在子线程中,监听子线程对应的RunLoop的状态

<b style="color:red">
注意:

</b>

// 点击屏幕,开启子线程
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{

    // 创建线程
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(doSomething) object:nil];
    // 启动线程
    [thread start];

}


/**
在子线程中,创建RunLoop并添加timer和observer
*/
-(void)doSomething
{
    //①获取当前的运行循环--子线程对应的运行循环---如果没有会懒加载创建
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();

    //②每个运行循环至少要一个source或者timer
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(haha) userInfo:nil repeats:YES];

    //③给运行循环添加监听器
    //---创建observer-监听所有的状态
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    
    /**
     *  kCFRunLoopEntry = (1UL << 0),  当RunLoop启动的时候
        kCFRunLoopBeforeTimers = (1UL << 1),  即将处理计时器
        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(@"即将处理计时器");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"即将处理source");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"即将进入睡眠状态");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"进入睡眠状态");
            break;
        case kCFRunLoopExit:
            NSLog(@"RunLoop退出");
            break;
    }

    
    
});

    //---添加监听器
    CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes);


    //④启动运行循环
    CFRunLoopRun();

}

<b style="color:orange">
相关函数解析:

1 创建观察者 CFRunLoopObserverCreateWithHandler

第一个参数:分配方式(可以理解为内存空间分配方式)-使用默认的 CFAllocatorGetDefault() 
第二个参数:需要监听RunLoop的哪些状态--一般监听所有状态 kCFRunLoopAllActivities
第三个参数:是否持续监听
第四个参数:优先级--一般传0
第五个参数:当监听的状态发生改变时调用,回调block

2 添加观察者 CFRunLoopAddObserver

第一参数:运行循环
第二个参数:观察者
第三个参数:添加的模式,一般选择kCFRunLoopCommonModes

3 启动运行循环 CFRunLoopRun()

</b>

9 常驻线程

正常情况下,子线程创建后,当子线程对应的任务执行完毕后,子线程就会被销毁。假设有这样的需求:启动某个子线程,这个子线程一致存在,我们可以随时通过点击按钮(或者其他source)在该线程上面执行其他任务。这样的子线程就是常驻线程。

<b style="color:red">
实现的原理:当创建子线程后,就开启一个运行循环,因为运行循环是一个死循环,所以该线程就会一直常驻,不会被销毁。

注意:为了保证子运行循环不会退出循环,至少需要一个source或者timer。
</b>

10 autoreleasepool在什么时候销毁和创建

第一次创建: 启动RunLoop的时候

最后一次销毁:RunLoop退出的时候

因为自动释放池可能会在某个时间清理一下,所以在整个程序运行过程中,还可能出现创建和时候autoreleasepool。一般是在RunLoop即将进入休眠时,销毁。即将唤醒的时候创建。

上一篇下一篇

猜你喜欢

热点阅读