ios runloop总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ios runloop总结相关的知识,希望对你有一定的参考价值。
参考技术A 面试回答方式:通常来讲,一个线程在执行完任务后,会直接退出。 但在我们日常的App使用中,即使什么都不做,App也不会退出。这其中的原理,便是在线程中构建一个消息循环,使得这个线程中一直有任务执行。在ios和OSX中,这个消息循环的机制便被称为RunLoop。
RunLoop 可以保持程序的持续运行,它负责处理事件和消息,可以让线程在没有消息的时候休眠,在有消息的时候才唤醒做事, 以节省CPU资源。
iOS系统中,提供了2套API来访问和使用RunLoop。
CoreFoundation框架中的CFRunLoopRef对象,它提供了纯C函数的API,是线程安全的。苹果开源了CFRunLoopRef
Foundation框架中的NSRunLoop对象,它是基于CFRunLoopRef的封装,但是这些API不是线程安全的
其实我们在使用runloop的时候大多可以使用CFRunLoopRef进行操作
一、runloop和线程有什么关系呢?
我们需要看一下runloop,主线程会始终维护一个runloop,那么子线程呢,由于苹果是不允许直接创建runloop的,所以runloop的获取提供了两个方式CFRunLoopGetMain() 和 CFRunLoopGetCurrent(),而这两个函数的内部实现是先为主线程创建一个runloop,子线程的话是通过传入的key来获取一个runloop,没有获取到则创建一个,所以子线程如果不获取runloop则子线程便不存在runloop
所以:
1.线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。
2.子线程刚创建时并没有RunLoop,如果你不主动获取,那它一直都不会有。
3.RunLoop 会在第一次获取时创建,在线程结束时销毁。
4.主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
二、runloop的结构
关于runloop有五个类,分别是
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopObserverRef
CFRunLoopTimerRef
而这几个类内部结构都是一个结构体,在每一个而结构体中都有一个 pthread_t _pthread;属性则可以看出 runloop和线程是一一对应关系,每个runloop内部会保留一个对应的线程
其中 CFRunLoopModeRef 的结构体如下
struct __CFRunLoopMode
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
;
其中
CFRunLoopSourceRef是事件的触发器
CFRunLoopModeRef是当前的runloop的mode选择,苹果公开提供的Mode有两个:kCFRunLoopDefaultMode(NSDefaultRunLoopMode)和UITrackingRunLoopMode
CFRunLoopTimerRef是基于时间的触发器
CFRunLoopObserverRef是事件变化的观察者
说道这里即可————————
runloop的实际应用场景:
1.如事件的监听
比如IM中消息的长按应该出现复制,转发等弹框操作,但是对消息Label本身区域进行长按水平滑动又需要出现放大镜弹框显示放大文字,这种不同事件的响应可以采用CFRunLoopObserverRef来进行判断
如:
currentMode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain());
//设置手势
[self setupGestures];
__weak __typeof(self)weakSelf = self;
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault,kCFRunLoopAllActivities,YES,0,^(CFRunLoopObserverRef observer,CFRunLoopActivity activity)
__strong __typeof(weakSelf)strongSelf = weakSelf;
if(strongSelf==nil)
return;
CFComparisonResult rest = CFStringCompare(strongSelf->currentMode,CFRunLoopCopyCurrentMode(CFRunLoopGetMain()),kCFCompareBackwards);
if(rest != kCFCompareEqualTo)
strongSelf->currentMode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain());
if((NSString *)CFBridgingRelease(strongSelf->currentMode)== UITrackingRunLoopMode)
//正在长按横向滑动/拖拽
[strongSelf scrollDidScroll];
);
CFRunLoopAddObserver(CFRunLoopGetMain(),observer,kCFRunLoopCommonModes);
2.定时器的使用
3.创建一个常驻线程
说白了就是一直跑一个NSRunloop让线程保活,不用时候休眠(这也是性能优化的一个点喔!)
比如我有一个for循环,循环内部又有一个在主线程很耗时的操作,此时我想开一个子线程来处理耗时,但是循环一次,线程就开辟一次,不断的开辟线程去性能是十分损耗的,但是上述说过,子线程不主动获取runloop是不存在的,所以此时我们可以让runloop一直对线程保活。
引申,其实这里单独开一个小的线程去执行某个action的话可以使用NSThread的
4.发现和消除卡顿
5.app 异常捕捉
附:
runloop内部底层实现可以参考上述文章链接进行更深的讲解
如:RunLoop 的核心是基于 mach port 的,其进入休眠时调用的函数是 mach_msg()其实这里就是更底层之间的消息传递
以上是关于ios runloop总结的主要内容,如果未能解决你的问题,请参考以下文章