CFRunLoop
的概念简单来讲,
CFRunLoop
对象负责监控事件输入源以及对其进行分发管理。CFRunLoop
管理的类型一般分为sources(CFRunLoopSource
)、timers(CFRunLoopTimer
)和observers(CFRunLoopObserver
)三种类型。git
CFRunLoop
的使用CFRunLoopSource
CFRunLoopSourceRef
是产生事件的地方。Source
包括Source0
和Source1
两个版本。github
Source0
:主要由应用程序管理,它并不能主动触发事件。使用时,你须要先调用CFRunLoopSourceSignal(source)
,将这个Source
标记为待处理,而后手动调用CFRunLoopWakeUp(runloop)
来唤醒RunLoop
,让其处理这个事件。一般咱们使用的也是Source0
事件。bash
Source1
:主要因为RunLoop
和kernel
进行管理。包含了一个mach_port
和一个回调(函数指针),被用于经过内核和其余线程相互发送消息。这种Source
能主动唤醒RunLoop
的线程。async
- (void)cfSource {
//建立上下文
CFRunLoopSourceContext context = {};
context.perform = runLoopSourceCallback;
context.info = (__bridge void *)self;
//建立source
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
//添加source
CFRunLoopAddSource(runLoop, source, kCFRunLoopCommonModes);
NSLog(@"create source in %@", [NSDate date]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//执行source相关事件
CFRunLoopSourceSignal(source);
CFRunLoopWakeUp(runLoop);
CFRelease(source);
});
}
// source回调
static void runLoopSourceCallback(void *info) {
NSLog(@"reiceive source in %@", [NSDate date]);
}
//执行结果
create source in Wed Mar 25 16:22:08 2020
reiceive source in Wed Mar 25 16:22:11 2020
复制代码
根据上面的执行结果,可见,对于Source0
事件,咱们必须调用CFRunLoopSourceSignal
方法去标记为“待处理”事件,对于CFRunLoopWakeUp
能够根据具体状况调用,若是当前RunLoop
是处于运行状态,不调用也是OK的,但为了不当前RunLoop
可能处于休眠状态,最好加上。函数
CFRunLoopTimerRef
CFRunLoopTimerRef
是基于时间的触发器。和NSTimer
相似,能够执行一些定时任务。oop
CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, // 用于分配内存,一般使用kCFAllocatorDefault便可
CFAbsoluteTime fireDate, // 第一次触发调用的时间
CFTimeInterval interval, // 回调间隔
CFOptionFlags flags, // 苹果备用参数,传0便可
CFIndex order, // RunLoop执行事件的优先级,对于Timer是无用的,传0便可
CFRunLoopTimerCallBack callout, // 回调callback
CFRunLoopTimerContext *context); // 用于与callback联系的上下文context
复制代码
- (void)cfTimer {
self.timerCount = 0;
//建立上下文
CFRunLoopTimerContext context = {};
context.info = (__bridge void*)self; //将当前对象做为参数传入
CFRunLoopRef runloop = CFRunLoopGetCurrent();
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + 1, //第一次回调的时间,则设置为1s之后
3, //回调时间间隔
0, 0, &timerFiredCallback, &context);
// 设置运行时间偏差范围
CFRunLoopTimerSetTolerance(timer, 0.1);
CFRunLoopAddTimer(runloop, timer, kCFRunLoopCommonModes);
CFRelease(timer);
NSLog(@"start timer in %@", [NSDate date]);
}
static void timerFiredCallback(CFRunLoopTimerRef timer, void *info) {
ViewController *controller = (__bridge ViewController *)info;
NSLog(@"recieve timer event with count: %@, in %@", @(controller.timerCount), [NSDate date]);
if (++controller.timerCount == 5) {
CFRunLoopTimerInvalidate(timer); //关闭定时器
}
}
复制代码
//执行结果
start timer in Wed Mar 25 16:42:59 2020
recieve timer event with count: 0, in Wed Mar 25 16:43:00 2020
recieve timer event with count: 1, in Wed Mar 25 16:43:03 2020
recieve timer event with count: 2, in Wed Mar 25 16:43:06 2020
recieve timer event with count: 3, in Wed Mar 25 16:43:09 2020
recieve timer event with count: 4, in Wed Mar 25 16:43:12 2020
复制代码
根据结果,能够看到第一次回调是在1s以后,剩余的回调都是每隔3s回调一次。布局
CFRunLoopObserverRef
CFRunLoopObserverRef
:观察者,主要用于观察RunLoop
的状态变化,以便在不一样状态时作一些操做。能够经过CFRunLoopObserverCreateWithHandler
和CFRunLoopObserverCreate
方法建立观察对象,前者是经过block方式回调,后者是经过C函数callback方式。post
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
};
复制代码
RunLoop
的kCFRunLoopBeforeWaiting
和kCFRunLoopExit
状态,回调处理相关操做,以实现利用RunLoop
空闲状态时作一些额外的操做。- (void)runBlockWhenMainThreadIdle {
__weak typeof(self) wSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"start submit block in %@", [NSDate date]);
[wSelf runWithBlock:^{
NSLog(@"finish block when main thread is idle in %@", [NSDate date]);
}];
});
}
- (void)runWithBlock:(void(^)(void))block {
CFRunLoopActivity flag = kCFRunLoopBeforeWaiting | kCFRunLoopExit; //监听RunLoop即将休眠和退出的状态
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, flag, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
//回调操做
if (block) {
block();
}
//移除相关监听
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
}
复制代码
经过监听
RunLoop
进入休眠和结束前的状态,来执行布局更新操做。ui
/// Update layout and selection before runloop sleep/end.
- (void)_commitUpdate {
#if !TARGET_INTERFACE_BUILDER
_state.needUpdate = YES;
[[YYTransaction transactionWithTarget:self selector:@selector(_updateIfNeeded)] commit];
#else
[self _update];
#endif
}
复制代码
@interface YYTransaction()
@property (nonatomic, strong) id target;
@property (nonatomic, assign) SEL selector;
@end
static NSMutableSet *transactionSet = nil;
static void YYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
if (transactionSet.count == 0) return;
NSSet *currentSet = transactionSet;
transactionSet = [NSMutableSet new];
[currentSet enumerateObjectsUsingBlock:^(YYTransaction *transaction, BOOL *stop) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[transaction.target performSelector:transaction.selector];
#pragma clang diagnostic pop
}];
}
static void YYTransactionSetup() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
transactionSet = [NSMutableSet new];
CFRunLoopRef runloop = CFRunLoopGetMain();
CFRunLoopObserverRef observer;
observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(),
kCFRunLoopBeforeWaiting | kCFRunLoopExit,
true, // repeat
0xFFFFFF, // after CATransaction(2000000)
YYRunLoopObserverCallBack, NULL);
CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes);
CFRelease(observer);
});
}
@implementation YYTransaction
+ (YYTransaction *)transactionWithTarget:(id)target selector:(SEL)selector{
if (!target || !selector) return nil;
YYTransaction *t = [YYTransaction new];
t.target = target;
t.selector = selector;
return t;
}
- (void)commit {
if (!_target || !_selector) return;
YYTransactionSetup();
[transactionSet addObject:self];
}
- (NSUInteger)hash {
long v1 = (long)((void *)_selector);
long v2 = (long)_target;
return v1 ^ v2;
}
- (BOOL)isEqual:(id)object {
if (self == object) return YES;
if (![object isMemberOfClass:self.class]) return NO;
YYTransaction *other = object;
return other.selector == _selector && other.target == _target;
}
@end
复制代码
为子线程手动添加自动释放池。经过监听
RunLoop
中的kCFRunLoopEntry
状态,保证执行前插入NSAutoreleasePool
,而后经过监听kCFRunLoopBeforeWaiting | kCFRunLoopExit
状态,保证RunLoop
进入睡眠或结束时,释放相关对象。atom
static NSString *const YYNSThreadAutoleasePoolKey = @"YYNSThreadAutoleasePoolKey";
static NSString *const YYNSThreadAutoleasePoolStackKey = @"YYNSThreadAutoleasePoolStackKey";
static const void *PoolStackRetainCallBack(CFAllocatorRef allocator, const void *value) {
return value;
}
static void PoolStackReleaseCallBack(CFAllocatorRef allocator, const void *value) {
CFRelease((CFTypeRef)value);
}
static inline void YYAutoreleasePoolPush() {
NSMutableDictionary *dic = [NSThread currentThread].threadDictionary;
NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];
if (!poolStack) {
/* do not retain pool on push, but release on pop to avoid memory analyze warning */
CFArrayCallBacks callbacks = {0};
callbacks.retain = PoolStackRetainCallBack;
callbacks.release = PoolStackReleaseCallBack;
poolStack = (id)CFArrayCreateMutable(CFAllocatorGetDefault(), 0, &callbacks);
dic[YYNSThreadAutoleasePoolStackKey] = poolStack;
CFRelease(poolStack);
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // create
[poolStack addObject:pool]; // push
}
static inline void YYAutoreleasePoolPop() {
NSMutableDictionary *dic = [NSThread currentThread].threadDictionary;
NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];
[poolStack removeLastObject]; // pop
}
static void YYRunLoopAutoreleasePoolObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
switch (activity) {
case kCFRunLoopEntry: {
YYAutoreleasePoolPush();
} break;
case kCFRunLoopBeforeWaiting: {
YYAutoreleasePoolPop();
YYAutoreleasePoolPush();
} break;
case kCFRunLoopExit: {
YYAutoreleasePoolPop();
} break;
default: break;
}
}
static void YYRunloopAutoreleasePoolSetup() {
CFRunLoopRef runloop = CFRunLoopGetCurrent();
CFRunLoopObserverRef pushObserver;
pushObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopEntry,
true, // repeat
-0x7FFFFFFF, // before other observers
YYRunLoopAutoreleasePoolObserverCallBack, NULL);
CFRunLoopAddObserver(runloop, pushObserver, kCFRunLoopCommonModes);
CFRelease(pushObserver);
CFRunLoopObserverRef popObserver;
popObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopBeforeWaiting | kCFRunLoopExit,
true, // repeat
0x7FFFFFFF, // after other observers
YYRunLoopAutoreleasePoolObserverCallBack, NULL);
CFRunLoopAddObserver(runloop, popObserver, kCFRunLoopCommonModes);
CFRelease(popObserver);
}
@implementation NSThread (YYAdd)
+ (void)addAutoreleasePoolToCurrentRunloop {
if ([NSThread isMainThread]) return; // The main thread already has autorelease pool.
NSThread *thread = [self currentThread];
if (!thread) return;
if (thread.threadDictionary[YYNSThreadAutoleasePoolKey]) return; // already added
YYRunloopAutoreleasePoolSetup();
thread.threadDictionary[YYNSThreadAutoleasePoolKey] = YYNSThreadAutoleasePoolKey; // mark the state
}
@end
复制代码
这里还有一点值得参考的是,releasepool
的存储位置是放在了线程的私有空间threadDictionary
中。另外关于子线程中操做是否须要手动进行释放,能够参考iOS 各个线程 Autorelease 对象的内存管理。我的以为加上是比较好的,毕竟官方文档并无明确说明子线程中是不须要加的。
RunLoop
调用方法主要集中在kCFRunLoopBeforeSources
和kCFRunLoopAfterWaiting
状态之间,能够经过开辟一个子线程来实时计算两个状态之间的耗时,看是否超过某个阈值,从而来判断主线程的卡顿状况。
@interface SMLagMonitor() {
int timeoutCount;
CFRunLoopObserverRef runLoopObserver;
@public
dispatch_semaphore_t dispatchSemaphore;
CFRunLoopActivity runLoopActivity;
}
@end
@implementation SMLagMonitor
#pragma mark - Interface
+ (instancetype)shareInstance {
static id instance = nil;
static dispatch_once_t dispatchOnce;
dispatch_once(&dispatchOnce, ^{
instance = [[self alloc] init];
});
return instance;
}
- (void)beginMonitor {
self.isMonitoring = YES;
//监测卡顿
if (runLoopObserver) {
return;
}
dispatchSemaphore = dispatch_semaphore_create(0); //Dispatch Semaphore保证同步
//建立一个观察者
CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities,
YES,
0,
&runLoopObserverCallBack,
&context);
//将观察者添加到主线程runloop的common模式下的观察中
CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
//建立子线程监控
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//子线程开启一个持续的loop用来进行监控
while (YES) {
long semaphoreWait = dispatch_semaphore_wait(dispatchSemaphore, dispatch_time(DISPATCH_TIME_NOW, STUCKMONITORRATE * NSEC_PER_MSEC));
if (semaphoreWait != 0) {
if (!runLoopObserver) {
timeoutCount = 0;
dispatchSemaphore = 0;
runLoopActivity = 0;
return;
}
//两个runloop的状态,BeforeSources和AfterWaiting这两个状态区间时间可以检测到是否卡顿
if (runLoopActivity == kCFRunLoopBeforeSources || runLoopActivity == kCFRunLoopAfterWaiting) {
//出现三次出结果
if (++timeoutCount < 3) {
continue;
}
// NSLog(@"monitor trigger");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSString *stackStr = [SMCallStack callStackWithType:SMCallStackTypeMain];
SMCallStackModel *model = [[SMCallStackModel alloc] init];
model.stackStr = stackStr;
model.isStuck = YES;
[[[SMLagDB shareInstance] increaseWithStackModel:model] subscribeNext:^(id x) {}];
});
} //end activity
}// end semaphore wait
timeoutCount = 0;
}// end while
});
}
- (void)endMonitor {
self.isMonitoring = NO;
[self.cpuMonitorTimer invalidate];
if (!runLoopObserver) {
return;
}
CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
CFRelease(runLoopObserver);
runLoopObserver = NULL;
}
#pragma mark - Private
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
SMLagMonitor *lagMonitor = (__bridge SMLagMonitor*)info;
lagMonitor->runLoopActivity = activity;
dispatch_semaphore_t semaphore = lagMonitor->dispatchSemaphore;
dispatch_semaphore_signal(semaphore);
}
@end
复制代码