前言
学如逆水行舟,不进则退!!共勉!!!
今天主要是来简单的分析一下iOSRunloop运行循环,主要是从以下四个方面分析:
runloo面试技巧p是什么?
1、runloop:runloo线程池的七个参数p是事件接收和分发机制的一个实现,是线程相关的基数组去重础框架的一部分
-
一个runloop就是一个事件处理循环,用来不停的调度工作及处理输出事件,当需要和该线程进行交互的时候才会使监控用runloop!
-
一个runloop有五种model。ios启动器runloop也叫运行循环,作用监控器什么牌子最好清晰度高是维持线程不退出,通过在内部维护事件循环来管理各种事件,包括定时器、用户交互、系统内部事件。当有消息需要处理时,运行循环被唤醒处理,监控家用远程手机处理完再进入休眠状态,等待下一次唤醒!监控家用远程手机
-
内监控家用远程手机部的事件循环是一个do-while 循环,do-whil面试e内部再插入接收消息的端口阻塞线程,使线程进入一种闲等待的状态,这是并不会消耗CPU资源。与咱们手动写的纯do-whilios鲁多多appe 循环还是有区别的,手动写的是忙等待,很消耗CPU资源。
-
能唤醒运行循环的消息有
-
- 通过端口接收系统内部事线程池的七个参数件(Source1)
- 用户交互事件响应 ,本质上也是通过端口唤醒(Source0)
- timer
-
Runloop状态六种状态 1、启动 2、退出 3、即将处理ti数组词多音字组词mer事件 即将处4、理sources事件 5、被唤醒监控摄像头 6、即将进入休眠
-
runloop有:So线程安全u监控器什么牌子最好清晰度高rce事件原、timer、observer 监听器、model
Source0用户主动发出的事件source1非用户主动发出的事件
注意:一次Runloop循环需数组c语言要最多渲染18张图片,一次runloop循环只渲染一张图片,循环18次来渲染18线程撕裂者张图片,runloop在运行的时候创建自动释放池,在休眠的时候释放
Runloop model
UI模式优先级最高,UI模式面试问题大全及答案大全被唤醒的时候,runloop只处理UI模式下的UI展示(ta监控系统bleView的滑动等)
UI模式 UITrackingRunLoopModel 只能被触摸才能唤醒 唤醒之后其他事件暂停
NSDefaultRunLoopModel数组词三声 默认模式 苹果建议放时钟 网络事件通常主线程是在这个Mode下运行。
NSRunLoopCommonModel不是一个真正的Runloop模式占位模式
GSEventReceiveRunLoopModel接收系统事件的内部model,通常ios系统用不到
UIinitializationRunLoopModel在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
2、线程和runloop面试问题大全及答案大全是什么关系
每条线程都有唯一的一个与之对应的runloop对象;主线程的runloop已经自动创建,子线程的runloop需要自动创建;runloop在第一次获取时创建,在线程结束时销毁
3、runloop的线程数model的作用
指定事件在运行循环中的优先级,线程的运行需要不同的数组的定义模式,去响应各种不同的事件,去处理不通情景模式。(比如可以优化tableView的时候设置UITrackingRunLoopModel下不进行一些操作,比如监控摄像头设置图片等)
二、面试题
1、滑动ScrollView时,NSTimer 为什么停止工作
滑动ScrollView时,主线程的RunLoop会切换到UITrackingRunL监控家用远程手机oopModel这个mo面试自我介绍简单大方del,执行的线程池的七个参数也是UITrackingRunLoopModel下的任务,而timer是添加监控器什么牌子最好清晰度高在NSDefaultRunLoopMode下的,所以time监控安装流程r任务不会执行,只有当UITrackingRunLoopModel的任务执行完毕,runloop切换到NSDefaultRunLoopModel后,才会继续 执行timer,保证TableVi线程数ew伤的定时ios模拟器器执行就要他把添加到U数组词三声ITrackingRunLoopModel中去执行
注意timer在不需要的时候,一定要记得调用invalidate的方法定时器失效,否则得不到释放
三、运行循环内部结构是什么样子的?
1. RunL数组词oop结构体的定义:
struct __CFRunLoop {
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp
_CFThreadRef _pthread; // 一个运行循环对应一个线程
CFMutableSetRef _commonModes; // commonModes 是一个伪模式,内部默认包含default & traking
CFMutableSetRef _commonModeItems; // commonModes里面包含的注册事件(timer/source/observer)
CFRunLoopModeRef _currentMode; // 当前正在使用的模式,同一时间只能使用一个mode
CFMutableSetRef _modes; // 运行循环下的所有模式
// ... 省略其他成员
};
2. RunLoopMode结构体的定义
struct __CFRunLoopMode {
// ... 省略其他成员
CFMutableSetRef _sources0; // source0,
CFMutableSetRef _sources1; // source1,
CFMutableArrayRef _observers; // 监听运行循环状态的监听者的数组
CFMutableArrayRef _timers; // 加在运行循环上的定时器的数组
__CFPortSet _portSet; // 接收消息的端口集
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
__CFPort _timerPort;
Boolean _mkTimerArmed;
#endif
};
1. RunLoopMode 的5种类型:
- kCF监控安装流程RunLoopDefaultMode:默认模式,主线程是在这个运行模式下运行
- UITrackingRunLoopM线程安全ode:跟踪用户交互事件
-
- 界面滑动时切换ios下载到这个mode, 可以保证滑动的时候不受其他 Mo数组去重de 影线程和进程的区别是什么响, 这就是iOS系统的ScrollView 滑动非常顺滑的原因!
- 因此,所有影响主线程 UI 滑动的操作,都放到default mode 上去执行。比如添加网络加载回来后线程池的图片到监控摄像头软件app下载 UITbaleview 的ImageView 上; 更新网络请求回来的数据到 tableview线程的几种状态 的数据源上.
- 防止tableview 加载图片导致滑动卡顿,也可以类似思路,将加载图片的方法放线程池到default mode 上去执行。
- 网络请求数据加载回来后,更新UI的动作也放在default mode, 这样不会影响用户正在滑动的体验。
- UIIniti数组指针alizationRunLoopMode:在刚启动 App 时第进入的第一个 Mode,启动完成后就不 再使用
- GSEventReceiveRunLoopMode:数组接受系统内部事件,通常用不到
- kCFRunLoopCommonMoios下载des:伪模式,不是一种真正的运行模式
-
- 是包含 Sou面试自我介绍3分钟通用rce/Timer/线程的概念Observer多监控器什么牌子最好清晰度高个 Mode中的一种解决方案,默认包含 kCFRunLoopDefaultM面试问题ode 和 UITrackingRunLoopMode。
- 如果把事件源添加到ios15 common mode,就相当于在Default Mode 和 Tracking Mode里都添加了这个事件源。在这两种模式下的运行线程是什么意思循数组环,都会执行该事件源。
- 也可以通过
CFRunLoopAddCom监控摄像头monMode(CFRunLoopRef rl, CFRunLoopMode mode);
添加新的mode进入common modes 中
2. RunLoopMode的事件源
1 CFRunLoopSource:线程池输入源/事件源 分为 source监控视频能保存多久0 和 source1 两种
- source1: Mach port驱动的事件,如CFMachport,CFMessagePort。可监听系统端口、内核、其他线程发送的消息,能主动唤醒 RunLoop,接收分发系统事件。 具备唤醒线程的能力。
- source0: 是用户触发的事件,如UIEvent,CFSocket。不能直接唤醒线程,需要借助source1 来唤醒线程,将当面试必问10大问题回答前线程从内核态切线程是什么意思换到用户态
2 CFRunLoopObserver: 监听者
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入运行循环
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理sources
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 已经退出运行循环
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
如何监听运行循环的事件?
- (void)viewDidLoad {
[super viewDidLoad];
[self.class addRunLoopObserver:(__bridge CFRunLoopRef)([NSRunLoop currentRunLoop])];
}
// RunLoop Observer
#pragma mark add Observer
+ (void)addRunLoopObserver:(CFRunLoopRef)runLoop {
static CFRunLoopObserverRef observer;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
CFOptionFlags activities = (kCFRunLoopAllActivities); // before exiting a runloop run
// allocator activities repeats order after CA transaction commits
observer = CFRunLoopObserverCreateWithHandler(NULL, activities, YES, 720, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"进入 RunLoop");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"刚从休眠中唤醒");
break;
case kCFRunLoopExit:
NSLog(@"退出 RunLoop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理Timer");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理Source");
break;
default:
break;
}
});
CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes);
CFRelease(observer);
});
}
- CFRunLoopTimer 定时器
NSTimer 是对 RunLoopTi数组指针mer 的封装, 可实现定时执行和延监控摄像头软件app下载迟执行的功能。
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
运行循环与线程、mode、注册事件的关系
从以上结构体的定义上可以看出:\
- 运行循环与线程是一对一的关系
- 运行循环内部有多个运行模式
- 运行模式内部有多种注册事件,其中source0,source1 只有1个,timer/observer 可能是多个。
四. 运行循环底层实现原理
- 运行循环底层是依赖于[Mach Port]来实现的。
- 在整个运行循环的流程中,会启动一个do-while的循环
- 循环内部通过
__CFRunLo线程数越多越好吗opServiceMachPort
函数创建了数组词拥有接收权限的端口来使线程处于等待唤醒的状监控摄像头态,这个等待过程于线程来讲是阻塞状态,对于运行循环来讲是休眠状态。所以运行循线程池环的休眠本质上就是线程被阻塞。 - 接收到端口的消息后面试自我介绍简单大方,解除线程的阻塞状态,也线程数越多越好吗就是运行循环被唤醒。之后判断唤醒线程安全端口类型:
-
- 是 timer 端口,执行 timer 回调线程的概念函数 __CFRunLoopDoTimers
- 是主线程端口面试自我介绍,执行主线程回调函数 CFRUNLOOP_IS_SERVICING_THE_M线程的概念AIN_DISPATCios14.4.1更新了什么H_QUEUE
- 不是以上2种,就认为是sou面试常见问题及回答技巧rce1端口,执行source1回调函数 __CFRunLo监控系统opDoSo数组排序urce监控1
3.1 运行循环执行流程图:
或者下面这样:
3.2 运行循环执行流程的伪代码:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
删减后的源码截图: 红色为执行具体动作,蓝色为回调监听事件
五. 主线程与子线程运行循环监控器什么牌子最好清晰度高的区别
六.运行循环的API
在iOS中,有 2 个 API 来处理运行循环:\
- NSRunLoop(位于Foundation框架)
- CFRunLoop(位于CoreFoundation框架)
- NSRu线程和进程的区别是什么nLoop 是 CFRunLoop 的面向对象 API 的封装,所以本质是一样的。
5.1 NSRunLoop run 的三种方线程式区别
开启某线程的Runloop,mode 中必iOS须具有timer、so面试问题urce、observer任一事件源才能开启
// 运行循环外还有一层循环包裹着,不能使用CFRunLoopStop 来停止 [NSRunLoop.currentRunLoop run]; [NSRunLoop.currentRunLoop runUntilDate:[NSDate distantFuture]]; /监控摄像头/ 只有一层循环,能使用CFRunLoopStop 来停止, 一般用来线程保活,因为可以手动控制停止 [NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];ios越狱
七. 运行循环的应用:
1.线程保活\
- 主线程的线程保活,获取主线程的modes, 开启一个死循环,里面遍历重新开启运行循环CFRunLoopRunInMode. 可以用来**[采集崩溃日志、防止因崩溃而造成的闪退]**现象发生
- 子线程的线程保活,在子线程上插入一个接收消息的端口,开启一个死循环,里面遍历重新开启运行循环CFRunLoopRunInMode。可以用来:
-
- 监控卡顿: 子线程不断地给主线程发送消息,记录应答时数组指针间,超过指定时间未应答,即可认为是卡顿。
- 节约开销: 当需要子线程频繁执行任务,多次创建线程产生大量的内存开销,开启子线程保活,可以节省一些开辟线程的开销。
- **AFN 防止下载完成之前,线程提退出导致 NSOperation 对象接收不到回调**:**AFN**创建单例的保活子线程,发起连接和接收回调,不占用主线程资源。
AFN常驻线程的部分源码:
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread =
[[NSThread alloc] initWithTarget:self
selector:@selector(networkRequestThreadEntryPoint:)
object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
注意: CFRunLoopRunInMode这个方法的参数 mode 不能是 kCFRunLoopCommonModes, 因为源码里面的逻辑就是这样,如果为k线程CFRunLoopCommonModes,直接返回kios是什么意思CFRunLoopRunFinished。
CFRunLoopRunSpeci数组词三声f面试自我介绍一分钟ic源码:
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (modeName == NULL || modeName == kCFRunLoopCommonModes || CFEqual(modeName, kCFRunLoopCommonModes)) {
...
return kCFRunLoopRunFinished;
}
....
}
2.优ios14.4.1更新了什么化TableView滚动体验 利用 CFRunLoopMode 的特性,可以将更新 UI 的动作放到 NSDefaultRunLoo线程撕裂者pMode 的 mo线程数de 里,数组c语言这样在滚动时 使用 UITrackingRunLoopMode ,NSDefaultRunLoopMode 这个 mode 的动作不会被执行。就不会因更新 UI 而影响到平滑滚动的效果。
- 图片加载完,更新 UI到 ImageView时,使用NSDefaultRunLoopMode, 不影面试必问10大问题回答响滑动
UIImage *downloadedImage线程池 = …; [self.avatarImageView performSelector:@selector(setImage:) withObject:downloadedImage afterDelay:0 inModes:@[NSDefaultRunLoopMode]];\
- 数据加载完,数组和链表的区别更新 UI到 tableview时,使用NSDefaultRunLoopMod监控系统e, 不影响滑动
[self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
赠人玫瑰,手留余香。如果有所收获,不如点赞支持一下吖。
iOS学习资料:领取地址
推荐阅读:面试宝典