美文网首页
iOS学习-Runloop

iOS学习-Runloop

作者: 快乐的tomato | 来源:发表于2021-05-12 22:26 被阅读0次

一、是啥

runloop,运行循环,程序运行的过程中循环做一些事情。
应用范围:

  • 定时器(timer)、PerformSelector、
  • GCD Async Main Queue
  • 事件响应、手势识别、界面刷新
  • 网络请求
  • AutoreleasePool

main.m里的main方法里就开启了Runloop,可以让程序不会马上退出,而是保持运行状态

int main(int argc, char * argv[]) {
    @autoreleasepool {
        //开启了runloop
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])
    }
}

也相当于下面的伪代码

int main(int argc, char * argv[]) {
    @autoreleasepool {

      int  retVal = 0;
      do{
           //睡眠中等待消息
           int message = sleep_and_wait();
           //处理消息
          retVal = process_message(message);
         }while(0==retVal)
    }
}

二、基本作用

1、保持程序持续运行
2、处理App中的各种事件,比入触摸事件,定时器事件
3、节省CPU资源,提高程序性能:该做事时做事,该休息休息

三、Runloop与线程之间的关系

1、每条线程都有唯一的一个与之对应的Runloop对象
2、Runloop保存在一个全局的Dictionary里,线程作为key,Runloop作为value
3、线程刚创建时并没有Runloop对象,Runloop会在第一次获取它时创建
4、Runloop会在线程结束时销毁

CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));//线程作为key,从字典里取出loop
    __CFUnlock(&loopsLock);
    if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);//没有loop,就创建一个
        __CFLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    if (!loop) {
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);//存loop存到字典
        loop = newLoop;
    }

上面是源码的一部分,说明了1、2、3

四、获取Runloop对象

iOS中有2套API来访问和使用RunLoop.
Foundation: NSRunloop
Core Foundation: CFRunloopRef
NSRunloop和CFRunloopRef都代表NSRunloop对象,是等价的。
NSRunloop是基于CFRunloopRef的一层OC包装。

[NSRunLoop currentRunLoop];//获取当前线程的runloop
 [NSRunLoop mainRunLoop];//获取主线程的runloop

五、RunLoop相关的类

主要的5个

  • CFRunloopRef
  • CFRunloopModeRef
  • CFRunloopSourceRef
  • CFRunloopTimerRef
  • CFRunloopObserverRef

看一下源码,了解一下他们之间的关系,只显示主要的

struct __CFRunLoop {
   
    CFRunLoopModeRef _currentMode;//当前模式
    CFMutableSetRef _modes;//模式的集合,里面装的是CFRunloopModeRef对象
};
struct __CFRunLoopMode {
    CFMutableSetRef _sources0;//里面装的是CFRunloopSourceRef对象
    CFMutableSetRef _sources1;//里面装的是CFRunloopSourceRef对象
    CFMutableArrayRef _observers;//里面装的是CFRunloopObserverRef对象
    CFMutableArrayRef _timers;//里面装的是CFRunloopTimerRef对象
};

用图表示


关系图.png

一个RunLoop有若干个model,一个model里有sources0、sources1、observer、timers

六、CFRunloopModeRef

1、CFRunloopModeRef代表Runloop的运行模式
2、一个RunLoop有若干个model,一个model里有sources0、sources1、observer、timers
3、RunLoop启动时只能选择其中的一个model,作为currentMode
4、如果需要切换mode,只能退出当前loop,再重新选择一个mode进入,这样做的好处:不同组的sources0、sources1、observer、timers能分割开来,互不影响
5、如果mode里没有任何sources0、sources1、observer、timers,RunLoop会立马退出

6.1 Mode的五种模式

常用的几个:
1、kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
2、UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
5、kCFRunLoopCommonModes: 这是一个占位用的Mode,并不是一种真正的Mode,默认包括Default和Tracking模式。

3、UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
4、GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

6.2 source0

Source0是用来处理触摸事件、Perfrom Selectors
代码看一下


image.png
6.3 source1

Source1是用来处理基于Port的 线程之间的通信、系统事件捕捉

6.4 timers

timers 处理 定时器 NSTimer

6.5 observer

监听器,监听runLoop的状态、UI刷新、AutoreleasePool

七、Runloop运行逻辑

image.png

八、应用

1、解决NSTimer在滑动时停止工作的问题

代码如下

static int count = 0;
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        
        NSLog(@"%d",++count);
    }];

运行期间我滑动了,tableView,打印结果如下


image.png

看到中间隔了10秒,才打印,为什么呢?上述方法是直接添加到DefaultMode下工作,滑动的时候是在TrackingMode下进行的,解决如下

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 
                           repeats:YES 
                          block:^(NSTimer * _Nonnull timer) {
        
        NSLog(@"%d",++count);
  }];
//timer 在CommonModes下工作
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

九、相关问题

1、讲讲Runloop,项目中有用到吗?
2、Runloop内部实现逻辑?
3、Runloop和线程的关系?
4、程序中添加每3秒相应一次的NSTimer,当拖动tableView时,timer可能无法响应要怎么解决?
5、Runloop是怎么响应用户操作的,具体流程是什么样的?
6、Runloop的几种状态?
7、Runloop的mode作用是什么?

相关文章

网友评论

      本文标题:iOS学习-Runloop

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