美文网首页
iOS RunLoop简单理解与运用

iOS RunLoop简单理解与运用

作者: 大大盆子 | 来源:发表于2017-04-24 12:01 被阅读50次

二话不说先上我麦来压压惊


什么是RunLoop?

RunLoop顾名思义运行着的循环,而且是一个死循环,他负责监听几乎所有的事件(触摸事件,网络事件,时钟事件),当监听到了事件就会唤醒线程去执行,没事件线程就睡觉。

RunLoop常见的模式

RunLoop在同一时间中只能响应一种模式下的事件,同时会关闭上一种模式;不同的事件都有对应的模式,处理事件都会切换到事件对应的模式。
<ul>
<li> NSDefalutRunLoopMode //默认模式
<li> UITrackingRunLoopMode //拖动事件,例如:UIScrollView
<li> NSRunLoopCommonModes //占位模式,包含前面两个

RunLoop跟线程的关系

  • RunLoop跟线程是一一对应的,不能主动创建,只能通过获取来拿到RunLoop对象

  • 在主线程中RunLoop是默认创建开启的,来保证线程不死,程序不退出;在分线程中,默认没有创建RunLoop,线程在执行完任务之后就会退出,当我们在线程中获取RunLoop时,它才会创建,一旦run起来,这个分线程将跟主线程一样不会主动退出。

      开启一个线程,并创建一个定时器
      
      NSThread *thread = [[NSThread alloc]initWithBlock:^{
         NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
              NSLog(@"哈哈");
          }];
          [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
          //1.[[NSRunLoop currentRunLoop] run];
          //2.while (!_isFinished) {
          //  [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
          //}
          NSLog(@"你看,来了吗?");
       }];
      [thread start];
    

    来看看运行结果:

    发现定时器并没有执行回调方法!

    原因:子线程中默认是不开启RunLoop的,所以线程里面代码执行完之后,线程就销毁了。

    解决:让线程常驻 -> 开启RunLoop。

    1. 直接run -> [[NSRunLoop currentRunLoop] run];但是在执行run方法之后,在当前线程中后面的代码将永远不会再执行,比如NSLog(@"你看,来了吗?");,因为RunLoop会一直在那里循环,所以这个线程将永远不会再自动销毁,只能通过执行[NSThread exit]来暴力退出,RunLoop也会随之关闭,当然线程中后续代码更加不会再执行。

    2. 手动添加一个循环来控制RunLoop循环,runUntilDate:同run方法开启RunLoop,只不过是加了一个时间限制,在未来多少秒暂停,这样通过手动控制RunLoop循环就可以控制线程的生命周期,当关闭此循环,Runloop也会停止,然后会执行线程剩余代码,之后线程销毁,这样做的好处就是可控制。

      while (!_isFinished) {
          
             [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
          }
      

RunLoop优化

RunLoop如何优化?我们都知道耗时操作放到子线程中执行,更新UI放到主线程执行(在子线程中更新UI也会等到子线程所有操作执行完了然后再到主线程中执行UI操作,因为UIKit是线程不安全的:UI控件都是用的原子属性->效率高。),那么如果更新UI耗时长怎么办?其实是因为RunLoop在一次循环当中执行了过多的复杂的UI操作,我们需要对UI操作做拆分,然后增加RunLoop的循环次数来分步执行,一次循环只做一个UI更新模块,从而保证RunLoop的流畅性。

- (void)addRunLoopObserver{
    
    //添加RunLoop监听,需要使用CFRunLoop,因为NSRunloop没有这个功能
    
    //1.获取runloop
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    
    //2.1创建上下文
    CFRunLoopObserverContext context = {
        
        0, //这个context的版本
        (__bridge void *)(self), //传入的参数,这里我传入控制器
        CFRetain, //告诉它retain是调用哪个函数
        CFRelease, //告诉它release是调用哪个函数
        nil,
    };
    
    //2.2创建runloop观察者
    /*
     CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator,
                                                 CFOptionFlags activities,
                                                 Boolean repeats,
                                                 CFIndex order,
                                                 CFRunLoopObserverCallBack callout,
                                                 CFRunLoopObserverContext *context);
     
     @param allocator:这个参数用来分配空间给新的对象。默认情况下使用NULL或者kCFAllocatorDefault。
     @param activities:设置Runloop的运行阶段的标志,当运行到此阶段时,CFRunLoopObserver会被调用
     @param repeats:CFRunLoopObserver是否循环调用,false为单词调用,否则循环调用。
     @param order:CFRunLoopObserver的优先级,当在Runloop同一运行阶段中有多个CFRunLoopObserver时,根据这个来先后调用CFRunLoopObserver。正常情况下使用0。
     @param callout:回调函数
     @param context:CFRunLoopObserver结构体里面的一个结构体,它主要用来给回调函数传递消息的。
     @return CFRunLoopObserverRef:观察者指针对象
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(NULL,
                                                            kCFRunLoopBeforeWaiting,
                                                            YES, 0,
                                                            callBack,
                                                            &context);
    
    //3.给runloop添加观察者
    CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes);
    
    
    //4.通过定时器来增加runloop循环次数
    [NSTimer scheduledTimerWithTimeInterval:0.001
                                     target:self
                                   selector:@selector(timerAction)
                                   userInfo:nil
                                    repeats:YES];
}

- (void)timerAction{
    //不执行任何代码,只作为一个触发runloop监听回调的功能。
}

/**
 回调函数,在kCFRunLoopBeforeWaiting(runloop等待执行循环之前)情况下调用,因为定时器给的是0.001秒,所以这里调用非常频繁,保证一次循环执行一个任务

 @param observer 观察者对象
 @param activity Runloop的运行阶段的标志
 @param info CFRunLoopObserverContext传入的参数
 */
void callBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    
    //1.拿到传过来的参数,再进行转换,因为这是C函数,不能直接调用self,所以在添加观察的时候把self传过来。
    ViewController *vc = (__bridge ViewController *)(info);
    
    //2.执行拆分之后的UI模块(拆分的模块用task数组装起来,具体任务包装在block中,在这里只要执行block即可)
    if (vc.tasks.count == 0) {
        return;
    }
    RunLoopBlock block = vc.tasks.firstObject;
    !block?:block();
    [vc.tasks removeObjectAtIndex:0];
}
    

相关文章

  • iOS RunLoop简单理解与运用

    二话不说先上我麦来压压惊 什么是RunLoop? RunLoop顾名思义运行着的循环,而且是一个死循环,他负责监听...

  • iOS之RunLoop详解与实践

    目录 -RunLoop的概念 -RunLoop逻辑与实现 -RunLoop在iOS中运用 -RunLoop实践 -...

  • Runloop总结

    深入理解RunLoopMac&iOS之多线程CFRunLoop运用 RunLoop 的概念 runloop是一个对...

  • iOS知识点(10)RunLoop

    深入理解RunLoop iOS---实例化讲解RunLoop iOS runloop iOS-RunLoop充满灵...

  • runloop理解与运用

    什么是runloop? 个人理解为:他是一个运行循环,因为他的存在才会导致我们的运行的程序才不会挂掉,我们的事件处...

  • Runloop

    Runloop 实现原理及应用iOS - RunLoop 底层源码详解及具体运用

  • iOS笔记--RunLoop相关知识

    RunLoop相关知识 1.深入理解RunLoop 2.iOS 事件处理机制与图像渲染过程 3.iOS---Run...

  • 深入理解RunLoop

    深入理解RunLoop由 ibireme | 2015-05-18 | iOS, 技术RunLoop 是 iOS ...

  • 内存管理篇,RunLoop

    iOS内存管理 RunLoop理解

  • 深入理解RunLoop

    深入理解RunLoop 由ibireme| 2015-05-18 |iOS,技术 RunLoop 是 iOS 和 ...

网友评论

      本文标题:iOS RunLoop简单理解与运用

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