美文网首页
Runloop学习

Runloop学习

作者: tp夕阳武士 | 来源:发表于2019-08-14 18:59 被阅读0次

    1.RunLoop是什么东西?

    runloop:运行循环,官方文档中给出来的描述如下:


    • RunLoop对象处理来自窗口系统、端口对象和NSConnection对象的鼠标和键盘事件等源的输入。RunLoop对象还处理计时器事件。
    • 您的应用程序既不创建也不显式地管理RunLoop对象。每个线程对象都有一个RunLoop对象,根据需要自动为其创建。如果需要访问当前线程的运行循环,可以使用类方法current来访问。
    • 注意,从RunLoop的角度来看,计时器对象不是“输入”——它们是一种特殊类型,这意味着它们在触发时不会导致run循环返回。

    一般情况下我们几乎都很少接触到这个东西,但是runloop却又是和开发密切相关的;每一个iOS的APP在启动是都会启动一个runloop,就在main.m文件的入口函数里面:

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    //在UIApplicationMain(),函数的内部,其实已经启动了一个runloop
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
    //        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
            
            NSLog(@"appstart");
            
            int num = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
            
            NSLog(@"num:%d",num);
            
            return num;
        }
    }
    //当我们点进去UIApplicationMain()函数,我们可以发现它是返回int类型的
    //所以我们把函数改写成上述模式,可以发现NSLog(@"num:%d",num); 永远不会被执行
    

    2.如何访问RunLoop 框架?

    在iOS中有2套API可以用来访问RunLoop

    Foundation & Core Foundation

    NSRunLoop & CFRunLoopRef
    NSRunLoop & CFRunLoopRef 都代表RunLoop对象
    NSRunLoop 是基于CFRunLoopRef进行了OC的包装;

    苹果官方的RunLoop文档

    RunLoop.png

    从上图我们可以看出,runloop 接收事件来自两种不同的源,两种源都使用某一特定的处理例程来处理到达的事件:
    1.输入源(input source)

    输入源传递异步事件,通常消息来自于其他线程或程序;

    2.定时源(timer soucre)

    定时源则传递同步事件,发生在特定的时间或者重复的时间间隔.

    3.线程与RunLoop的关系

    • 线程与RunLoop是一一对应的关系,每一个线程都有一个唯一与其对应的RunLoop对象
    • 主线程的RunLoop是已经创建好了的,就在UIApplicationMain()这个方法里面,子线程的RunLoop需要主动创建,并且要手动调用run方法启动一下;
    • RunLoop在第一次获取的时候创建,在其对应线程结束的时候销毁
    3.1如何获得RunLoop对象?
    • Foundation框架,获取RunLoop的方式
    //获取当前线程的RunLoop
     NSRunLoop *currentRP = [NSRunLoop currentRunLoop];
    //获取主线程的RunLoop
     NSRunLoop *mainRP = [NSRunLoop mainRunLoop];
    

    *Core Foundation框架,获取RunLoop的方式

    //获取当前线程的runloop
    CFRunLoopRef currentRP =  CFRunLoopGetCurrent();
        
    //获取主线程的runloop
    CFRunLoopRef mainRP =  CFRunLoopGetMain();
    
    • NSRunLoop 转化成 CFRunLoopRef
    NSRunLoop *rp = [NSRunLoop currentRunLoop];
    CFRunLoopRef cp = rp.getCFRunLoop;
    
    3.2 在子线程中创建runloop对象:
    -(void)viewdidload{
      [super viewdidload];
       NSThread *td = [[NSThread alloc] initWithTarget:self selector:@selector(log:) object:@"aaa"];
       td.name = @"aaa";
       [td start];
    }
    
    -(void)log{
      NSThread *currentTD = [NSThread currentThread];//获取当前线程
      NSRunLoop *runlop = [NSRunLoop currentRunLoop];
      //获取当前的runloop,如果存在则获取,不存在会自动创建;
    }
    
    

    4. RunLoop的相关类

    Core Fundation中关于runloop的5个相关类:

    • CFRunLoopRef
    • CFRunLoopModeRef

    • CFRunLoopTimerRef
    • CFRunLoopSourceRef
    • CFRunLoopObsverRef
    runloop与runloopmode的关系.png

    不同的运行模式是为了分隔开不同组的<source/observer/timer> 在运行过程中不会互相干扰;

    4.1 CFRunLoopModeRef
    • CFRunLoopModeRef表示RunLoop的运行模式,一个mode里面有若刚soucre/observer/timer<至少有一个timer或者source>.
    • RunLoop有5种运行模式:
      1.kCFRunLoopDefaultMode<默认模式>
      2.UITrackingRunLoopMode <响应UI的时候是这种模式>
      3.UIInitializationRunLoopMode<刚启动app时进入的第一种模式,随后不再用>
      4.GSEventReceiveRunLoopMode<接收系统内部事件,通常不用>
      1. kCFRunLoopCommonModes<一种占位的模式,包含第一第二种模式>
    • RunLoop启动的时候,只能选择一种模式;
    • RunLoop如果要切换mode,需要先退出loop,在重新制定一种mode;
    4.2 CFRunLoopTimerRef
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor orangeColor];
        
        self.tv = [[UITextView alloc] init];
        [self.view addSubview:self.tv];
        [self.tv mas_makeConstraints:^(MASConstraintMaker *make) {
            make.center.mas_equalTo(0);
            make.width.mas_equalTo(300);
            make.height.mas_equalTo(150);
        }];
        self.tv.text = @"1;al\ndflkjadlfj\nasdf\nou\nlkej\nr\nl;\nkjoui\nasfljqeroui\ncv;lke\nroizv;l\nkjdf;klja\ndflafadf;jsad;lfjadf1;al\ndflkjadlfj\nasdf\nou\nlkej\nr\nl;\nkjoui\nasfljqeroui\ncv;lke\nroizv;l\nkjdf;klja\ndflafadf;jsad;lfjadf";
        
    }
    
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        
        [self.view endEditing:YES];
        
        NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(log:) userInfo:@"abc" repeats:YES];
        
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        
    }
    
    -(void)log:(NSString *)str{
        NSLog(@"%@---%@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode);
        
    }
    

    执行上述代码,在启动了timer的以后,一旦滑动textview,就会停止循环事件.
    原因:
    在滑动textview的时候,runloop切换了运行模式

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
         NSLog(@"%@---%@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode);
         //滑动视图的时候,runloop的运行模式是:UITrackingRunLoopMode
    }
    

    上文我们已经提到RunLoop如果要切换mode,需要先退出loop,在重新制定一种mode;
    如果我们想NSTimer的循环事件能够一直正常运行,则需要给在runloop添加timer的时候指定另一种运行模式:NSRunLoopCommonModes(这种运行模式同时包含了:default和common两种模式)

    4.2.1在子线程中创建NSTimer
    dispatch_async(dispatch_queue_create(0, 0), ^{
        NSTimer *t = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"当前线程:%@,%@",[NSThread currentThread],NSRunLoop.currentRunLoop.currentMode);
    }];
       [NSRunLoop.currentRunLoop addTimer:t forMode:NSRunLoopCommonModes];  
       [NSRunLoop.currentRunLoop run];
     });
    

    基于GCD封装的timer,不受runloopmode影响

    4.3 CFRunLoopSourceRef
    • CFRunLoopSourceRef 指的是runloop的输入源(input source)


      runloop_source.png

    以前的分类方法:

    • Port-Base Sources
    • Custom Input Sources
    • Cocoa Perform Selector Sources

    现在的分类方法:

    • Source0 : 非基于端口的事件源(一般是用户主动触发的事件,例如btn的点击,或者使用方法执行某些任务)
    • Source1 : 基于端口的事件源(可以理解为是系统调用的一些事件)
    4.4 CFRunLoopObsverRef
    • CFRunLoopObsverRef 是观察者,能够监听RunLoop的状态改变
    • CFRunLoopObsverRef 可以监听是时间点如下:
    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),//即将进入loop
        kCFRunLoopBeforeTimers = (1UL << 1),//即将处理timer
        kCFRunLoopBeforeSources = (1UL << 2),//即将处理source
        kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠
        kCFRunLoopAfterWaiting = (1UL << 6),//刚从休眠中唤醒
        kCFRunLoopExit = (1UL << 7),//即将退出loop
        kCFRunLoopAllActivities = 0x0FFFFFFFU
    }
    
    实现监听
    -(void)observer{
        /**
         创建runloop监听者
         参数1:怎么分配空间
         参数2:监听哪一种状态 kCFRunLoopAllActivities表示监听素有状态
         参数3:是否持续监听
         参数4:优先级 总是传0就可以
         参数5:监听到状态的回调
         */
        CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            switch (activity) {
                case kCFRunLoopEntry:
                    NSLog(@"即将进入runloop");
                    break;
                case kCFRunLoopBeforeSources:
                    NSLog(@"即将处理input source");
                    break;
                case kCFRunLoopBeforeTimers:
                    NSLog(@"即将处理timer source");
                    break;
                case kCFRunLoopBeforeWaiting:
                    NSLog(@"即将进入休眠");
                    break;
                case kCFRunLoopAfterWaiting:
                    NSLog(@"runloop被唤醒");
                    break;
                case kCFRunLoopExit:
                    NSLog(@"runoop退出");
                    break;
                default:
                    break;
            }
            
        });
        
        /**
         监听runloop
         参数1: 监听的是哪一个runloop
         参数2: observer 监听者
         参数3: mode 运行模式
         */
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    }
    
    

    5.RunLoop 处理逻辑

    runloop_逻辑图.png runloop_逻辑网友图.png
    官方版本
    官方描述.png

    相关文章

      网友评论

          本文标题:Runloop学习

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