OC底层知识(十) : RunLoop

简介: OC底层知识(十) : RunLoop

一、Runloop的简单介绍


Runloop  是在程序运行过程中循环做一些事情。比如应用在:定时器(NSTimer)、PerformSelector;GCD Async Main Queue;事件响应、手势识别、界面刷新、网络请求、AutoreleasePool

  • RunLoop的基本作用:保持程序的持续运行;处理App中的各种事件(如触摸事件、定时器事件等);节省CPU资源,提高程序性能:该做事时做事,该休息时休息


二、RunLoop对象的获取


  • 2.1、iOS中有2套API来访问和使用RunLoop
  • Foundation:NSRunLoop
  • Core Foundation:CFRunLoopRef
  • 2.2、NSRunLoop和CFRunLoopRef都代表着RunLoop对象
    NSRunLoop是基于CFRunLoopRef的一层OC包装
    CFRunLoopRef是开源的
  • 2.3、RunLoop与线程
  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
  • 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
  • RunLoop会在线程结束时销毁
  • 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
  • 2.4、获取RunLoop对象
  • OC(Foundation)获取RunLoop对象


[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
  • C(Core Foundation)获取RunLoop对象
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象


三、RunLoop相关的类


Core Foundation中关于RunLoop的5个类

CFRunLoopRef

CFRunLoopModeRef

CFRunLoopSourceRef

CFRunLoopTimerRef

CFRunLoopObserverRef

struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;                 /* locked for accessing mode list */
    __CFPort _wakeUpPort;                  // used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData;   // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};
  • 3.1、CFRunLoopModeRef
  • CFRunLoopModeRef代表RunLoop的运行模式
  • 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer


image.png

image.png


  • RunLoop启动时只能选择其中一个Mode,作为currentMode
  • 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
  • 不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
  • 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
  • CFRunLoopModeRef 2种Mode
  • kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
  • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响



image.png

3.2、RunLoop的运行逻辑


image.png

  • Source0
    触摸事件处理
    performSelector:onThread:
  • Source1
    基于Port的线程间通信
    系统事件捕捉
  • Timers
    NSTimer
    performSelector:withObject:afterDelay:
  • Observers
    用于监听RunLoop的状态
    UI刷新(BeforeWaiting)
    Autorelease pool(BeforeWaiting)


image.png


3.3、RunLoop休眠的实现原理



image.png


四、RunLoop在实际开中的应用 下面练习的demo


  • 4.1、控制线程生命周期(线程保活)
  • (1)OC核心代码实现保活 : 看demo里面的类JKCPermenantThread


self.innerThread = [[MJThread alloc] initWithBlock:^{
 // 线程保活
 // 往RunLoop里面添加 Source\Timer\Observer
 [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
  while (weakSelf && !weakSelf.isStopped) {
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  }
}];
  • (2)C核心代码实现保活:看demo里面的类ThreadCViewController


self.innerThread = [[JKThread alloc] initWithBlock:^{
   NSLog(@"begin----");
   // 创建上下文(要初始化一下结构体)
   CFRunLoopSourceContext context = {0};
   // 创建source
   CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
   // 往Runloop中添加source
   CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
   // 销毁source
   CFRelease(source);
   // 启动
   CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
   //            while (weakSelf && !weakSelf.isStopped) {
   //                // 第3个参数:returnAfterSourceHandled,设置为true,代表执行完source后就会退出当前loop
   //                CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
   //            }
   NSLog(@"end----");
 }];
  • 4.2、解决NSTimer在滑动时停止工作的问题(我在做一个cell上动画的时候用了NSRunLoop来防止tableview滑动的时候cell上的动画暂停)


NSTimer  *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(clickTimeIndexPath) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
/*
  UITrackingRunLoopMode和NSDefaultRunLoopMode是真正存在的模式
  [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
  [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
  NSRunLoopCommonModes:n并不是一个真的模式,它只是一个标记
  timer 能在 _commandModes 数组中存放的模式下工作
  [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
 */


五、RunLoop的几个面试题


  • 5.1、讲讲 RunLoop,项目中有用到吗?(你可以回答里面的任意一个)
    答:我用到了:解决NSTimer在滑动时停止工作的问题(我在做一个cell上动画的时候用了NSRunLoop来防止tableview滑动的时候cell上的动画暂停)
  • 5.2、runloop内部实现逻辑?
    答:看上面的 3.2、RunLoop的运行逻辑
  • 5.3、runloop和线程的关系?
    答:看上面的 2.3、RunLoop与线程
  • 5.4、timer 与 runloop 的关系?
    答: timer 是运行在runloop里面的,runloop控制 timer什么时候执行
  • 5.5、程序中添加每3秒响应一次的NSTimer,当拖动tableview时timer可能无法响应要怎么解决?
    答:看上面的4.2,也就是要添加 NSRunLoopCommonModes标记
  • 5.6、runloop 是怎么响应用户操作的, 具体流程是什么样的?
    答: 由 source1捕获触摸或者响应事件,再由 source0去处理触摸或者响应事件
  • 5.7、说说runLoop的几种状态


image.png

5.8、runloop的mode作用是什么?


答:runloop的mode 常用的有两种mode: NSDefaultRunLoopModeUITrackingRunLoopMode,在某一种mode下执行自己的source0、source1、timer,不同mode下互不影响,分工明确,使用起来更灵活、流畅。

目录
相关文章
|
存储 机器学习/深度学习 缓存
我惊了!!!ThreadLocal 源码存在内存泄露的 Bug!!!
我惊了!!!ThreadLocal 源码存在内存泄露的 Bug!!!
147 0
|
C语言 iOS开发
OC 底层知识(二): KVO
OC 底层知识(二): KVO
188 0
OC 底层知识(二): KVO
|
Android开发 iOS开发
OC:跟随码哥一起学习KVO
KVO的全称 Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变。本文将带你全面了解KVO实现、原理、底层逻辑,以及相关拓展
188 0
OC:跟随码哥一起学习KVO
|
安全 Unix 程序员
OC底层知识(十一) : 多线程
OC底层知识(十一) : 多线程
199 0
OC底层知识(十一) : 多线程
|
API
OC底层知识(三): KVC
OC底层知识(三): KVC
185 0
OC底层知识(三): KVC
|
监控 调度 iOS开发
[OC RunLoop_翻译]一、介绍 & 二、剖析运行循环
[OC RunLoop_翻译]一、介绍 & 二、剖析运行循环
136 0
[OC RunLoop_翻译]一、介绍 & 二、剖析运行循环
|
存储 iOS开发 C++
OC 底层知识(一):OC的本质
OC 底层知识(一):OC的本质
247 0
OC 底层知识(一):OC的本质
|
存储 安全 编译器
OC底层知识(十二) : 内存管理
OC底层知识(十二) : 内存管理
195 0
OC底层知识(十二) : 内存管理
|
API
OC底层知识(五) :关联对象
OC底层知识(五) :关联对象
306 0
OC底层知识(五) :关联对象
|
iOS开发
iOS开发多线程篇 - NSOperation(下)
iOS开发多线程篇 - NSOperation
iOS开发多线程篇 - NSOperation(下)