美文网首页
主题六《RunLoop》

主题六《RunLoop》

作者: 东方奇迹 | 来源:发表于2020-09-19 23:19 被阅读0次

    (1)、什么是RunLoop

    截屏2020-07-12 下午10.10.26.png 截屏2020-07-12 下午10.12.39.png 截屏2020-07-12 下午10.13.46.png 截屏2020-07-12 下午10.15.09.png 截屏2020-07-12 下午10.16.04.png

    (2)、RunLoop资料

    (3)、RunLoop与线程

    • 每条线程都有唯一的一个与之对应的RunLoop对象
    • 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
    • RunLoop在第一次获取时创建,在线程结束时销毁

    (4)、获得RunLoop对象

    截屏2020-07-12 下午10.42.34.png 截屏2020-07-12 下午10.48.14.png 截屏2020-07-12 下午10.52.30.png

    (5)、RunLoop相关类

    截屏2020-07-12 下午10.56.37.png 截屏2020-08-02 下午1.14.27.png 截屏2020-08-02 下午1.18.35.png

    (6)、RunLoop与相关类的关系

    截屏2020-07-12 下午11.07.55.png 截屏2020-08-02 下午1.19.03.png

    解析:
    RunLoop启动的时候必须要选择一种运行模式,当RunLoop选择了一种运行模式之后它会做一个判断做一个检查,它要检查一下这个运行模式是否为空,主要检查这个运行模式里面有没有source有没有timer,如果一个source也没有一个timer也没有它就认为这个运行模式为空,运行模式为空是这个RunLoop就马上退出了,如果运行模式不为空,里面有source或者有timer,那么这个RunLoop就启动了,也就是说这个死循环就开起来了,它就是这样运作的。注意检查的时候只会判断source和timer,它是不会判断observer的。

    model(运行模式)说明:

    截屏2020-07-12 下午11.18.52.png 截屏2020-07-20 下午6.06.12.png 截屏2020-07-20 下午6.24.27.png 截屏2020-07-20 下午6.27.43.png 截屏2020-07-20 下午6.31.40.png 截屏2020-07-20 下午6.33.02.png

    (7)、GCD中的定时器

    截屏2020-07-20 下午8.48.54.png

    (8)、CFRunLoopSourceRef

    截屏2020-07-20 下午8.56.48.png 截屏2020-07-20 下午9.09.50.png

    (8)、CFRunLoopObserverRef

    截屏2020-07-21 上午9.47.28.png
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        //01 创建观察者
        /**
         参数说明
         第一个参数:分配存储空间 默认
         第二个参数:要监听的状态
         第三个参数:是否要持续监听 YES
         第四个参数:0
         第五个参数:block回调 当runloop状态改变的时候会调用
         */
        
        CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            
            /**
            kCFRunLoopEntry = (1UL << 0),
            kCFRunLoopBeforeTimers = (1UL << 1),
            kCFRunLoopBeforeSources = (1UL << 2),
            kCFRunLoopBeforeWaiting = (1UL << 5),
            kCFRunLoopAfterWaiting = (1UL << 6),
            kCFRunLoopExit = (1UL << 7),
            kCFRunLoopAllActivities = 0x0FFFFFFFU
             */
            
            switch (activity) {
                case kCFRunLoopEntry:
                    NSLog(@"runloop启动");
                    break;
                case kCFRunLoopBeforeTimers:
                    NSLog(@"runloop即将处理timer事件");
                    break;
                case kCFRunLoopBeforeSources:
                    NSLog(@"runloop即将处理source事件");
                    break;
                case kCFRunLoopBeforeWaiting:
                    NSLog(@"runloop即将进入到休眠");
                    break;
                case kCFRunLoopAfterWaiting:
                    NSLog(@"runloop被唤醒");
                    break;
                case kCFRunLoopExit:
                    NSLog(@"runloop退出");
                    break;
    
                default:
                    break;
            }
            
        });
    
        //02 监听runloop的状态
        /**
         参数说明
         第一个参数:runloop对象
         第二个参数:监听者
         第三个参数:监听runloop在哪种运行模式下的状态
         NSDefaultRunLoopMode == kCFRunLoopDefaultMode
         NSRunLoopCommonModes == kCFRunLoopCommonModes
         */
    
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
        
        [NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            
            NSLog(@"timer------");
            
        }];
    
    }
    
    

    (9)、RunLoop的运行流程

    RunLoop处理逻辑-网友整理版

    20160811202022658.png

    每次运行runloop,你的线程的runloop会自动处理之前未处理的消息,并通知相关的观察者。runloop的时间队列具体的顺序如下:

    1、通知观察者runloop已经启动
    2、通知观察者任何即将要开始的定时器
    3、通知观察者任何即将启动的非基于端口的源
    4、处理任何准备好的非基于端口的源
    5、如果基于端口的源准备好并处于等待状态,则立即处理,跳至步骤9
    6、通知观察者线程进入休眠
    7、将线程置于休眠直到任一下面的事件发生:

    • 某一事件达到基于端口的源
    • 定时器启动
    • runloop设置的时间已经超时
    • runloop被显式唤醒

    8、通知观察者线程将被唤醒
    9、判断唤醒的事件类型并处理:

    • 如果是用户定义的定时器事件,则处理并重新开始Runloop,跳至步骤2
    • 如果是一个事件源,则传递事件并重新开始Runloop,跳至步骤2
    • 如果被显式唤醒,并且还没超时,则重新开始Runloop,跳至步骤2

    10、通知观察者runloop结束。

    截屏2020-08-02 下午3.59.41.png 截屏2020-08-02 下午3.10.49.png 截屏2020-07-21 下午12.18.16.png 截屏2020-08-02 下午5.32.11.png

    (10)、RunLoop的应用

    截屏2020-07-21 下午12.22.05.png 截屏2020-07-21 下午2.23.47.png
    //如何开启一个常驻线程
    - (IBAction)createNewThreadBtnClick:(id)sender {
        
        //01 创建线程对象
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run1) object:nil];
        //02 启动线程
        [thread start];
        
        self.thread = thread;
    }
    - (IBAction)goOnBtnClick:(id)sender {
        
        //让之前创建的子线程继续执行任务 主线程-子线程
        [self performSelector:@selector(run2) onThread:self.thread withObject:nil waitUntilDone:YES];
        
    }
    //该方法执行完毕,线程对象就会进入到死亡状态
    - (void)run1 {
        NSLog(@"---run1---%@",[NSThread currentThread]);
        
        //01 子线程的runloop需要手动创建 + 启动
        //02 runloop启动之后,选择运行模式(默认),判断运行模式是否为空
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        
        //往运行模式中添加source | timer
        /**
        [NSTimer scheduledTimerWithTimeInterval:3.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            
            NSLog(@"tiemrunning");
            
        }];
         */
        
        //source = port | custom | selector
        //为默认的运行模式添加一个port事件,目的是让运行模式不为空,把runloop开启起来
        [runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
        
        //内部会指定运行模式为默认
        [runloop run];
    
        NSLog(@"---end---");
    }
    - (void)run2 {
        NSLog(@"---run2---%@",[NSThread currentThread]);
    
    }
    
    

    (11)、RunLoop自动释放

    截屏2020-07-21 下午3.38.47.png

    (12)、RunLoop面试题

    截屏2020-07-21 下午3.40.35.png 截屏2020-07-21 下午3.42.42.png 截屏2020-08-02 下午12.52.00.png 截屏2020-07-20 下午9.09.03.png 截屏2020-07-20 下午9.16.56.png

    1、什么是RunLoop,基本作用是什么?

    字面意思:

    • 运行循环、跑圈
    • 其实它内部就是do-while循环,在这个循环内部不断的处理各种任务(比如source、timer、observer)

    基本作用:

    • 保持程序持续运行不退出
    • 处理APP中各种事件(触摸事件,定时器事件,Selector事件)
    • 节省CPU资源,提高程序性能,该做事的时候做事,该休息的时候休息

    2、RunLoop与线程之间的关系?

    • RunLoop与线程是一一对应的关系
    • 主线程的RunLoop已经创建,子线程的RunLoop需要主动创建
    • RunLoop第一次调用时创建,线程销毁时退出

    3、RunLoop五大相关类及其之间的关系?

    • CFRunloopRef
    • CFRunloopModeRef
    • CFRunloopTimerRef
    • CFRunloopSourceRef
    • CFRunloopObserverRef

    RunLoop启动的时候必须要选择一种运行模式,当RunLoop选择了一种运行模式之后它会做一个判断做一个检查,它要检查一下这个运行模式是否为空,主要检查这个运行模式里面有没有source有没有timer,如果一个source也没有一个timer也没有它就认为这个运行模式为空,运行模式为空是这个RunLoop就马上退出了,如果运行模式不为空,里面有source或者有timer,那么这个RunLoop就启动了,也就是说这个死循环就开起来了,它就是这样运作的。注意检查的时候只会判断source和timer,它是不会判断observer的。

    4、RunLoop的运行逻辑?

    • 1、RunLoop启动
    • 2、即将处理timer
    • 3、即将处理source
    • 4、处理source0
    • 5、如果有source1,跳转到第九步
    • 6、即将进入休眠
    • 7、休眠中,等待被唤醒
    • 8、结束休眠被唤醒
    • 9、处理唤醒时收到的消息,timer消息和source1消息,然后跳转到第二步
    • 10、退出RunLoop

    5、RunLoop的应用有哪些?

    1、 开启一个常驻线程,让子线程不被消亡,等待其它线程发来消息,处理消息事件

    (1)在子线程中开启一个定时器
    (2)在子线程中进行一些长期的监控

    2、 可以控制定时器在特定模式下执行

    3、 可以让某些事件(行为、任务)在特定模式下执行

    4、 添加observer监听RunLoop状态

    5、 自动释放池的创建与销毁

    (1)自动释放池第一次创建:当RunLoop启动的时候
    (2)自动释放池最后一次销毁:当RunLoop退出的时候
    (3)自动释放池其它时间的创建和销毁:当RunLoop将要进入休眠的时候,会把之前的自动释放池释放掉,重新创建一个新的自动释放池

    相关文章

      网友评论

          本文标题:主题六《RunLoop》

          本文链接:https://www.haomeiwen.com/subject/phzyyktx.html