• RunLoop机制理解


    一、浅识RunLoop

      RunLoop在开发中我们一直在用,但是没有注意他。要想理解RunLoop,首先我们需要先了解一下程序运行机制。

      程序运行机制:我们都知道OC是运行时语言,也就是说对象的类型是在程序运行的时候确定的。并调用类与对象相应的方法。但是最终代码的执行始终是面向过程的。线程也是一样:一个线程从开始代码执行,到结束代码销毁。app如何实现这样的机制:app从运行开始一直处于待命状态,接收到事件之后执行操作,操作完成后继续等待相应,直到程序终止运行。这样的管理线程执行任务的机制就是RunLoop机制。线程在执行中的休眠和激活就是由RunLoop对象进行管理的。

    二、RunLoop与线程的关系

      RunLoop是用来管理线程的。每一个线程都有一个RunLoop对象。可以通过具体的方法去获得。但是需要注意:虽然每一个线程都可以获取RUnLoop对象,但是并不是每一个线程中都有实例对象,我们可以这样理解:如果我们不获取RunLoop,这个RunLoop就不存在,我们获取时,如果不存在,就会去创建。在主线程中,这个MainRunLoop是默认创建并运行激活的。

    三、NSRunLoop

      NSRunLoop是Cocoa框架中的类,与之对应的是在Core Fundation中有一个CFRunLoopRef类。这两者的区别是前者不是线程安全的,而CFRunLoopRef是线程安全的。

    1、获取主线程的NSRunLoop

    //获取主线程的NSRunLoop
    +(NSRunLoop *)mainRunLoop;

    2、获取当前线程的NSRunLoop

    //获取当前线程的RunLoop:有的话就直接获取,没有的话就自动创建
    +(NSRunLoop *)currentRunLoop;

    3、NSRunLoop的执行模式

    //获取当前runloop的执行模式
    @property(readonly,copy)NSString *currentMode;
    //两种执行模式
    //默认的模式,接收大部分输入源的响应
    NSString *const NSDefaultRunLoopMode;
    //多种模式的集合
    NSString *const NSRunLoopCommonModes;

    4、获取RunLoop的CFRunLoopRef对象

    //获取RunLoop的CFRunLoopRef对象
    -(CFRunLoopREF)getCFRunLoop;

    5、将定时器添加到RunLoop中

    //将定时器添加到runloop中
    -(void)addTimer:(NSTimer *)timer forMode:(NSString *)mode;

    6、获取下个响应时间

    -(NSDate *)limitDateForMode:(NSString *)mode;

    定时器的执行,其实并不是按照时间段额间隔进行调用方法,而是在定时器注册到RunLoop中后,RunLoop会设置一个一个的时间点进行调用,例如,5,10,15,20等等。如果错过了某个时间点,定时器并不会延迟调用,而是直接等待下一个时间点调用,所以定时器并不是准确的。

    7、在某个时间期限前接收相应

    -(void)aceptInputForMode:(NSString *)mode beforeDate:(NSDate *)limitDate;

    8、开始运行

    -(void)run;

    9、到某个时间点运行

    -(void)runUntilDate:(NSDate *)limitDate;

    10、在某个期限前运行

    -(BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;

    11、

    - (void)addPort:(NSPort *)aPort forMode:(NSString *)mode;
    
    添加输入源端口到runloop中,NSPort对象可以理解为详细的载体,会传递消息与其代理。
    
    
    
    - (void)removePort:(NSPort *)aPort forMode:(NSString *)mode;
    
    将某个输入源端口移除

    四、RunLoop的调用

    一般情况下我们很少去显式调用或者启动RunLoop,但是下边的情况需要手动设置。

    1、在分线程中使用定时器

    定时器的实现是基于RunLoop的,平时我们使用定时器或许并没有对RunLoop做什么操作,那是因为主线程的RunLoop默认是开启运行的,如果我们进行如下操作:

    -(void)viewDidLoad{
      [super viewDidLoad];
      dispatch_queue_t queue = dispatch_queue_create("myQueue",DISPATCH_QUEUE_CONCURRENT);
     dispatch_async(queue,^{
        NSTimer *timer = [NSTimer scheduledTimerWithTimerInterval:1 target:self selector:@selector(time) userInfo:nil repeats:YES];
    });     
    }
    -(void)time{
    NSLog(@"runTimer");
    }

    此时运行,控制台不会输出runTimer。我们必须在线程中手动的执行如下代码:

    [[NSRunLoop currentRunLoop] run];

    这样定时器才可以正常工作。

    2、当线程中使用如下的方法时

    某些延迟函数和选择器在分线程中的使用,我们必须手动开始RunLoop

    @interface NSObject (NSDelayedPerforming)
    
    - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
    
    - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
    
    + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
    
    + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
    
    
    - (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
    
    - (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(id)arg;
    - (void)cancelPerformSelectorsWithTarget:(id)target;

    五、其他

    输入源被注册到RunLoop中时会有方法进行remove。但是定时器没有remove,但是它的invalidate方法可以将其从RunLoop中移除。invalidate是重要的也是唯一的将定时器从RunLoop中注销的方法,所以如果我们创建了定时器,就一定要再不适用的时候调用invalidate方法。

    六、附

    参考自:http://my.oschina.net/u/2340880/blog/491966

  • 相关阅读:
    win7下apache+mysql+php安装配置
    mysql -- 外键及数据的完整性
    mysql -- 索引的使用
    mysql 字符类型
    mysql 常用命令
    mysql 时间类型
    URI与URL
    Flask基本问题
    Session和Cookie的区别与联系
    Python flask关于新闻项目业务逻辑梳理
  • 原文地址:https://www.cnblogs.com/zhanggui/p/4781566.html
Copyright © 2020-2023  润新知