iOS中的RunLoop


Runloop

在iOS开发中,有两个很重要的概念,RunLoop和RunTime,在这里就说一下RunLoop,什么是RunLoop,顾名思义,run就是运行,loop就是循环的意思.Runloop就是循环运行。程序运行本身是一个进程。Runloop实现了APP处于随时待命的状态:来处理一些用户的事件:用户的点击事件,UI刷新事件,timer事件,Selector事件都是Runloop的作用,他可以用较少的资源占用来实现一些功能。

Runloop和线程有什么关系?

相信很多人在面试的时候都被问到过这个问题,那么我们就来用代码一点点解释这个问题,首先,我们创建一个子线程,在子线程中去调用@selector方法函数


/// 创建了一个子线程,在子线程去调用doSomething方法
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self performSelector:@selector(doSomeThing) withObject:nil];
    });
- (void)doSomeThing{
    
    NSLog(@"结束了");
}

相信大家也都看了代码,其实在这段代码中,doSomeThing函数中的打印并不会执行,这是为什么呢?首先,因为[self performSelector:@selector(doSomeThing) withObject:nil]这个方法是基于Timer来实现的,而Timer的运行又是基于Runloop来实现的,而在线程中没有开启的Runloop,所以此方法不会执行。在子线程中,Runloop和线程是一一对应的关系。想要让这个方法执行,就需要在子线程中创建一个Runloop,并且运行。
那么有人就会说了,如果将这个方法放在主线程中,会不会被执行呢,答案是会的。从源码我们可以知道,源码提供了两个获取Runloop的CFRunLoopGetMain( ) 和 CFRunLoopGetCurrent( )的方法,每一个Runloop都是一个对象,他们被存放在一个全局的可变字典当中。子线程中,Runloop随着线程的结束而被销毁。而主线程中,获取主线程的时候,是通过懒加载的方式,如果没有,就会默认创建一个mian_runloop.
currentRunlopp底层是怎么实现的,也是CFRunloopGetCurrent 从字典中获取,最终走向runloopget0,懒加载的形式创建的.

子线程中的RunLoop需要保活,主线程需要保活吗?为什么?

这时候就得说一下Runloop的组成,每一个RunLoop包含:Mode(runLoop的模式)Source0(处理用户事件),Source1(处理系统事件),Timer,以及一个Observer。通过源码,我们可以知道,runloop会不会被休眠。是通过两个参数来共同决定的,一个是count,一个是Mode、Source0、Source1、Timer是否有事件。如果是子线程的runloop,没有给runloop添加事件的时候,runloop处于休眠状态。
主线程中的runloop没有事件也不会被销毁,是因为虽然主线程中的Runloop,count>0确切来说是count=1;
runloop的启动有三种方式:

    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    // 第一种方式
    [runloop run];
    // 第二种方式
    [runloop runUntilDate:<(nonnull NSDate *)>];
    // 第三种方式
    [runloop runMode:<(nonnull NSRunLoopMode)> beforeDate:<(nonnull NSDate *)>];

通过源码我们得知,第一种启动方式和第二种启动方式,都是在循环中调用第三种启动方式,苹果官方API也说明:第一种是无条件开启RunLoop这样就会导致我们不能将当前开启的RUnLoop停止下来,就是因为有循环存在。所以我们一般采用第三种开启方式。

如果有叫霍雪琼的看到此文章,记得打赏作者,谢谢。


以上是对遇到此问题的一个笔记,仅供参考,欢迎讨论。


文章作者: 小小学生
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小小学生 !
评论
  目录