runloop总结
1. runloop简介
runloop是一种高级的循环机制,让程序持续运行,并处理程序中的事件,让线程在须要的时候忙起来,不须要的时候休眠。
主线程的runloop包管应用程序的存活,从而可以实时接收到用户的响应,可以或许触发事件。
2. runloop的基本作用
保持程序持续运行,节省CPU资源,提高程序性能。
3. 获取runloop的流程
程序开始时第一次获取runloop的时候,一定获取的是主线程的runloop。在获取runloop的函数中会定义一个全局的字典变量,该字典是以线程为key,获取的runloop为value,生存到字典中。方便之后获取runloop的时候可以先遍历字典找一找之前有没有获取。如果字典没有对应的runloop,再获取对应线程的runloop。
4. runloop和线程的关系
- 每条线程都有唯一与之对应的runloop对象。
- 线程刚创建的时候,并没有runloop对象,runloop会在第一次获取它的时候创建。
- 主线程默认开启自动开启runloop,子线程须要手动获取对应的runloop。
- 当线程竣事之后,runloop也会随之销毁。
5. runloop中的Mode有几种以及作用
runloop中有5种Mode:
- NSDefaultRunLoopMode: app默认的Mode,主线程就在这个状态下运行。
- UITrackingRunLoopMode: 界面跟踪,用于scrollview的触摸滑动,包管界面不会受其他Mode的影响。
- UIInitializationRunLoopMode:在程序刚启动的时候进入的第一个Mode,然后就切换到NSDefaultRunLoopMode。
- kCFRunLoopCommonModes: 这是一种标记Mode,事件可以运行在所有标记的Mode中,也可以添加观察者。
- GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer
常用的有三种Mode:
- NSDefaultRunLoopMode, 默认的模式, 有事件响应的时候, 会阻塞往事件。
- NSRunLoopCommonModes, 普通模式, 不会影响任何事件。
- UITrackingRunLoopMode, 只能是有事件的时候才会响应的模式。
6.runloop的事件源
输入源:通常用于处理基于文件描述符的事件,可以将外部事件与runloop关联起来,使runloop可以监听这些事件,而且举行相应的处理。
定时源:用于在指定时间隔断触发事件,实用于须要定期实验的任务。通过定时器在特定的时间触发回调方法。
7. 讲一下source0和source1
source0和source1都属于CFRunLoopSourceRef,而CFRunLoopSourceRef是对于输入事件源的抽象。
source0和source1都是在runloop中描述事件的触发机制。source0用于处理手动触发的事件,source1用于处理系统或者其他线程之间的事件。
source0(不会叫醒runloop):
- source0是非基于端口的事件源,用于处理应用程序内部生成的事变。
- 通常响应和处理一些特定的应用程序级事件,例如:触摸事件,用户交互事件。
- source0事件通常由应用程序自身生成和处理,不涉及线程间通讯。
source1(会叫醒runloop):
- source1是基于端口的事件源,用于处理与其他进程或者内核之间的交互。
- 通常处理一些系统级事件,如:网络数据,定时器事件。
- source1事件的处理涉及与其他进程或内核的通讯,比如举行网络数据的读写、处理文件描述符的状态变革等。
示例:
用户用手辅导击app界面时,会先触摸到硬件,屏幕会将这个事件先包装成Event,Event先告诉source1(mach_port)叫醒runloop,然后将这个事件Event分发给source0,让source0处理该事件。
8. runloop的六种观察者模式
- //观测的时间点有一下几个
- typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
- kCFRunLoopEntry = (1UL << 0), // 即将进入RunLoop
- kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理Timer
- kCFRunLoopBeforeSources = (1UL << 2), // 即将处理Source
- kCFRunLoopBeforeWaiting = (1UL << 5), //即将进入休眠
- kCFRunLoopAfterWaiting = (1UL << 6),// 刚从休眠中唤醒
- kCFRunLoopExit = (1UL << 7),// 即将退出RunLoop
- kCFRunLoopAllActivities = 0x0FFFFFFFU
- };
复制代码 9. 针对定时器在滑动时停止工作的问题
- 在创建定时器的时候,而且不做任何操作的时候,定时器处于NSDefaultRunLoopMode下。
- 当按住鼠标拖拽视图的时候,runloop就竣事了NSDefaultRunLoopMode,切换到UITrackingRunLoopMode,但是在这个模式下,并没有添加NSTimer,以是定时器就不工作了。
- 松开鼠标的时候,runloop就竣事UITrackingRunLoopMode模式,回到NSDefaultRunLoopMode模式,NSTimer就开始工作了。
10. 如何解决定时器不准确的原因
- runloop的循环过程中,NSTimer的触发事件阻塞,导致循环不能举行,延误了NSTimer的触发时间。
- runloop的循环过程中,主线程在某一时候发生阻塞,导致循环不能举行,延误了NSTimer的触发时间。
- 切换了runloop的运行模式,导致NSTimer在原有的模式下不能正常触发。
怎样解决:
- 只管克制将NSTimer放入容易阻塞的主线程中。
- 克制将耗时的操作放到NSTimer的线程中。
- 克制在NSTimer中举行耗时的操作,如果不能克制,就将操作转移到其他线程。
11. 什么是常驻线程,线程保活
- 常驻线程: 指在应用程序在运行期间始终存在的线程,用于实验持续运行的任务,这些线程通常不会由于任务实验完成而停止,而是持续运行以支持应用程序的正常运行。
- 线程保活:一种确保线程持续运行的机制,即使在没有任务的时候,也能包管线程存活。
多线程
1. 队多线程的明白
好处
- 使用多线程可以把占据时间长的任务放到后台去处理。
- 程序运行速度大概加快。
- 在等待任务(用户输入,网络收发数据)的实现上,多线程比力有效。
弊端
- 如果有大量线程,由于操作系统须要在他们之间切换。
- 更多的线程须要更多的内存空间。
- 线程的中断须要考虑对程序运行的影响。
- 须要防止程序死锁的环境。
2. iOS多线程的方式有哪几种
实现多线程的方法:
更倾向于GCD和NSOperation,封装更高级,使用起来更方便。
3. 有效过GCD吗
- //让处理在主线程中执行
- dispatch_async(dispatch_get main_queue(), ^{
- /*
- *只在主线程可以执行的处理
- *例如用户界面更新
- */
- });
-
复制代码- - (void)once { //GCD的一次性代码(只执行一次)
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- // 只执行1次的代码(这里面默认是线程安全的)
- });
- }
复制代码- - (void)after {
- NSLog(@"run -- 0");
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // 2秒后异步执行这里的代码...
- NSLog(@"run -- 2");
- });
- }
-
复制代码- dispatch_semaphore_t semalook = dispatch_semaphore_create(1);
-
- dispatch_async(dispatch_get_global_queue(0, 0), ^{
- dispatch_semaphore_wait(semalook, DISPATCH_TIME_FOREVER);
- NSLog(@"1开始");
- sleep(2);
- NSLog(@"1结束");
- dispatch_semaphore_signal(semalook);
- });
-
- dispatch_async(dispatch_get_global_queue(0, 0), ^{
- dispatch_semaphore_wait(semalook, DISPATCH_TIME_FOREVER);
- NSLog(@"2开始");
- sleep(2);
- NSLog(@"2结束");
- dispatch_semaphore_signal(semalook);
- });
复制代码 4. GCD的队列类型
可以使用dispatch_queue_create来创建对象,须要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并发队列。
- // 串行队列的创建方法
- dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
- // 并发队列的创建方法
- dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
复制代码 5. NSOperationQueue和GCD的区别以及上风
区别:
- GCD实验的效率更高,队列中实验的是由block构成的任务,写起来比力方便。
- GCD只支持FIFO的队列,但是NSOperationQueue可以设置最大并发数,设置优先级,添加依赖,调整顺序。
- NSOperationQueue可以在队列中设置依赖关系,但是GCD只能通过设置串行队列,或者在队列内添加barrier(dispatch_barrier_async)任务,才能控制实验顺序,较为复杂。
- NSOperationQueue由于面向对象,以是支持KVO,可以监测operation是否正在实验(isExecuted)、是否竣事(isFinished)、是否取消(isCanceld)
6. 线程安全的处理本领
- 互斥锁(Mutex):使用互斥锁可以确保同时时间只有一个线程访问共享资源,在Objective-C中,可以使用@synchronized关键字来创建互斥锁。
- @synchronized (self) {
- // 访问共享资源的代码
- }
复制代码
- 自旋锁(Spin Lock):自旋锁是一种忙等待锁,不断尝试获取锁,直到成功为止。在Objective-C中,可以使用os_unfair_lock来创建自旋锁。
- os_unfair_lock_t lock = &(OS_UNFAIR_LOCK_INIT);
- os_unfair_lock_lock(lock);
- // 访问共享资源的代码
- os_unfair_lock_unlock(lock);
复制代码
- 信号量(Semaphore):信量是一种数器,用于控制同时访问某个资源的线程数目,在Objective-C中可以使用dispatch_semaphore来创建信号量。
- dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
- // 访问共享资源的代码
- dispatch_semaphore_signal(semaphore);
复制代码
- 原子操作(Atomic Operations):原子操作是一种特的操作,可以确保对共享资源的读写操作是原子的,即不会被其他线程停止。在Objective-C中,可以使用atomic键字来声明属性原子类型。
- @property (atomic, strong) NSObject *sharedObject;
复制代码
- 串行队列(Serial Queue):使用串行队列可以确保任务按顺序实验,从而避多个线程同时访问共享资源。可以使用GCD(Grand Central Dispatch)来创建串行队列。
- dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue DISPATCH_QUEUE_SERIAL);
- dispatch_async(serialQueue, ^{
- // 访问共享资源的代码
- });
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |