iOS RunLoop简单理解与运用

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


什么是RunLoop?

RunLoop顾名思义运行着的循环,而且是一个死循环,本质是一个do,while循环,他负责监听几乎所有的事件(触摸事件,网络事件,时钟事件),当监听到了事件就会唤醒线程去执行,没事件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];
}
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容