iOS 详解RunLoop

Runloop :运行循环

  1. APP启动,操作系统会开启一条线程,这就是这个APP的主线程;
  2. 这个主线程是一个常驻线程,因为这条线程上边的Runloop 被开启了;

 

Runloop 作用

  1. 保证线程不退出;
  2. 负责监听所有的事件。 如: 触摸、时钟、网络事件...

 

Runloop 的模式(Mode)

  1. NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];
  2. [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];

 

    以上这两个timer的实现, 结果是: 函数1不会执行,函数2会执行。原因就是函数2内部已经添加了runLoop。而函数1, 需要手动添加。 那么函数1需要再添加: [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 即可执行time操作。

  •     Runloop共有5中模式:(常用的其实只有两种)
  •     NSDefaultRunLoopMode        // 默认模式
  •     UITrackingRunLoopMode        // UI模式, 优先级高于默认模式
  •     NSRunLoopCommonModes    // 占位模式,(并不是runloop 的真正模式,但功能相当于 默认模式+UI模式)
  •     程序初始化模式                         // 操作系统调用
  •     程序内核模式                             // 操作系统调用

 NSDefaultRunLoopMode  :  首先在没有事件发生时候runloop处于休眠状态,当有事件需要执行或响应, runloop会被唤醒做一次执行循环,执行结束继续处于休眠状态。 如果此时有触摸事件发生( 比如 拖拽事件或者触摸事件)那么runloop 会优先处理触摸事件,而忽略timer事件。timer 的selector 也不会被执行。当松开手 timer 会继续执行。

UITrackingRunLoopMode :简称 UI模式, runloop 只会触发UI事件,当触摸事件发生的时候才runloop 才会执行。而此时 默认模式即使有事件执行,runloop也不会理会。

NSRunLoopCommonModes: 占位模式,并不是lunloop真正的模式。 所谓占位指的是在添加了NSDefaultRunLoopMode 模式,也添加了TrackingRunLoopMode 模式。 那么此时表现也是二合一。 正常执行的timer在发生触摸时也会继续执行。但是!!!timer 中如果有耗时操作, 将会造成UI页面卡顿。 怎么解决呢? 当然是放在子线程中!

 

Runloop 与多线程

   NSThread *tread =  [[NSThread currentThread]initWithBlock:^{

        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];

       [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

       [[NSRunLoop currentRunLoop]run];

    }];

    [tread start];

 这样  当前子线程的runloop 会运行起来!子线程会一直存在,即使timer停止。 相当于一个死循坏!!!

那么要想销毁当前子线程,记得在子线程中调用  [NSThread exit];  

如果在主线程中调用  [NSThread exit], 那么主线程会退出, 子线程依旧还在运行。随着主线程的退出,页面点击、拖拽等事件都不会再响应。那为什么主线程挂掉之后为什么UI页面都还在显示? 因为UI显示是显示在操作系统上,UIApplication无法处理界面上的事情了,而界面是传递给主线程,主线程已挂断掉 此时runloop 不在处理界面上任何事情。

 

Runloop 的source

source: 事件源(输入源)按照函数调用栈分为

             source0:非系统内核事件;

             source1: 系统内核事件;

    

GCD timer, 直接上代码吧


    // 创建   
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    // 设置timer 时间单位是纳秒    1秒 = 1000000000纳秒
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0);
    // 设置回调
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"current thread ---   %@",[NSThread currentThread]);
    });
    // 启动
    dispatch_resume(self.timer);

根据执行结果可知, GCD timer  默认已经将Runloop 添加进去了。

 

Runloop 的Observer

用于观察runloop循环, 代码  :


- (void)viewDidLoad {
    [super viewDidLoad];

// 防止runloop 休眠
    [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(timerMeather) userInfo:nil repeats:YES];
 
   // 添加观察者
    [self addRunLoopObserver];
}

-(void)timerMeather
{
    
}

-(void)addRunLoopObserver{
    // 获取当前RunLoop
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    // 定义一个context
    CFRunLoopObserverContext context = {
        0,
        (__bridge void *)self,
        &CFRetain,
        CFRelease,
        NULL
    };
    // 定义观察者
    static CFRunLoopObserverRef defaultModeObserver;
    //创建观察者
    defaultModeObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &callBack, &context);
    // 添加观察者
    CFRunLoopAddObserver(runloop, defaultModeObserver, kCFRunLoopDefaultMode);
}

// 观察runloop回调
static void callBack(CFRunLoopObserverRef obderver,CFRunLoopActivity activity ,void *info){

  // info 当前控制器,  添加观察者时添加的context目的。
    NSLog(@"come ");
}


 添加代码会发现,刚启动的时候回执行几次,然后就不执行了,或者偶尔执行以下。 那是因为runloop 没任务执行,处于休眠状态。  那么我们给执行一个timer,让timer运行起来。 此时runloop 也就不会休眠了。

 

 

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章