RunLoop学起来很抽象,不容易理解,一定要多看几遍,多学才能学好!这也是中高级iOS必须掌握的知识点,也是面试中经常遇到的。
什么是RunLoop?
Run表示运行,Loop表示循环。结合在一起就是运行一个循环。RunLoop就是在程序运行过程中循环地做一些事情。
RunLoop的应用类别有哪些?
计时器,性能选择器
GCD异步主队列
响应、手势识别、界面刷新
网络请求
AutoreieasePool
这些底层由RunLoop支持。说白了,没有RunLoop支持,以上都无法实现。
如果没有RunLoop会怎么样?就像我们的命令行项目一样,在创建时默认没有RunLoop。请看下图。
因为没有RunLoop,所以程序会在到达第13行时自动退出。
并且我们iOS项目的Main函数中有UIApplicationMain (argc,argv,nil,appdelegateclassname这段代码,这里是创建一个主线程的RunLoop,所以我们的程序不会退出,它一直在运行。我们可以大致将主函数中的伪代码写成如下:
RetVal这等于0。当没有事件处理时,RunLoop会休眠,类似于睡眠。一旦有事件需要处理,比如点击刷新事件,process_message就会处理这个事件。当它完成后,它会继续休息。当retVal=0时,程序将一直执行,不会退出。这是RunLoop的功能。
RunLoop的基本功能
1.保持程序持续运行。
2.处理App中的各种事件(如触摸事件、定时器事件等。)
3.节省CPU资源,提高程序性能:该做事时做事,该休息时休息。
…
获取RunLoop对象
iOS中有两组API可以访问和使用RunLoop:
基础:NSRunLoop (OC语言中的OC)
核心基础:CFRunLoopRef (C语言中的C)
NSRunLoop和CFRunLoopRef都表示RunLoop对象。
NSRunLoop是一层基于CFRunLoopRef的OC包装器
CFRunLoopRef是开源的。(CFRunLoopRef参考链接)
其实我们很多人都是被OC包装的。请看下文:
获取当前运行循环
获取当前运行循环和主线程运行循环。
获取RunLoop
这里注意“地址不一样”是因为NSRunLoop是对CFRunLoopDef的一层封装,可以使用OC的nsrog(“%@”,[NSRunLoop MainRunLoop])。相比之下,它的地址是C语言获得的地址。主线程只有一个RunLoop。
运行循环和线程
每个线程都有一个唯一的RunLoop对象与之对应(一一对应)。
RunLoop存储在全局字典中,thread作为键,RunLoop作为值。
第一次创建线程时没有RunLoop对象,第一次获取线程时会创建RunLoop。
RunLoop在线程结束时被销毁。
主线程的RunLoop已经自动创建,子线程默认不打开RunLoop。
看一眼源代码:CFRunLoopGetCurrent
由于源代码不能像objc那样直接打开,所以我们把它拉到项目里来检查。
从字典也能看出来是一对一的关系。而且确实是第一次获取的时候是空的,然后再去创建这个 RunLoop。从字典上也可以看出,是一对一的关系。而且第一次得到的时候真的是空,然后创建这个RunLoop。
然后让我们继续理解RunLoop内部的数据结构以及它实际上是如何工作的。
RunLoop相关类
核心基础中关于RunLoop的五个类
1.CFRunLoopRef
2.CFRunLoopModeRef
3.CFRunLoopSourceRef
4.CFRunLoopTimerRef
5.CFRunLoopObserverRef
再次查看CFRunLoopRef的底层源代码:
就是上面的结构。我们可能用的是红色。这些。pthread是线程,每个runloop都会保存这个东西。最后一个_modes,这个是一个***,CFMutableSetRef。我们可以认为自己的一套也是一套。比如NSMutableSet也是一个***,所以this _modes里面存储了一堆模式。
这个模式是CFRunLoopModeRef类型,所以它存储了一堆CFRunLoopModeRef类型的模式。
而_currentMode也是CFRunLoopModeRef的类型,所以我们很容易得出一个结论:
一个RunLoop对象里面有一堆模式,也就是它存在于_modes中,只有一个是_currentMode。
我们再来偷看一下源代码,看看mode里存储了什么?
所以我们来总结一下图片:
运行模式有很多种,对应的_currentMode只有一种。
CFRunLoopModeRef
1.CFRunLoopModeRef,代表RunLoop的运行模式;
2.一个RunLoop包含几个模式,每个模式包含几个
source 0/source 1/timer/observer;
3.当3。RunLoop启动时,只能选择一种模式作为当前模式;;
4.如果需要切换模式,只能退出当前的RunLoop,然后重新选择一种模式进入;
5.不同组的
source 0/source 1/timer/observer可以分开,互不影响;
6.如果Mode中没有
source 0/source 1/timer/observer,RunLoop会立即退出;
如果只能在一种模式下运行,对性能等都有很大的好处。比如我在滑动模式的时候,不考虑非滑动模式,不会卡死,会流畅很多。还要注意,它在循环中切换模式,所以它不会导致程序退出。
常见的有两种模式,其他情况很少,所以掌握这两种模式一般没问题。
1.kcfrunloopdefaultMode(nsdefaultrunloopMode):app的默认模式,通常主线程都是在这个模式下运行的;
2.UITrackingGrunloopMode:界面跟踪模式,用于ScrollView视图跟踪触摸滑动,保证界面滑动时不受其他模式影响;
RunLoop到底是做什么的?
RunLoop一直执行到底做了什么?其实RunLoop在不断循环的时候,就是在处理每种模式下的Source0,Source1,Timer,Observer的事件。那么我们来看看对应的事件到底是什么。
Source0
触摸事件,执行选择器:线程上:
比如我们的touchbegin,我们来看下面这段代码:
源1
线程间基于端口的通信和系统事件的捕获。
(两个线程之间传递消息的处理,系统事件捕获,实际上也包括触摸事件,只是捕获事件然后传递给Source0)。
计时器
Ntimer timer,
perform selector:with object:after delay(此方法的底层实现也由Ntimer实现)。
观察员
用于监控RunLoop、UI刷新(等待前)、自动释放池(等待前)的状态。
(UI刷新、自动释放池释放等。将在RunLoop进入睡眠之前执行)
这些东西完全是我们平时在开发中写的代码,比如设置背景色,设置边框等等。
因为RunLoop的知识点比较多,写的太多不利于大家的阅读和消化,其他内容后面再介绍!
本文来自掩于岁月投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/654281.html