一、Runloop的定義
Runloop就是運(yùn)行時(shí)循環(huán),保證程序一直運(yùn)行下去
- Runloop實(shí)際上是一個(gè)對(duì)象律姨,這個(gè)對(duì)象用于處理程序運(yùn)行過(guò)程中遇到的各種事件(觸摸,UI刷新臼疫,定時(shí)器择份,performSelector等等),保證程序的持續(xù)運(yùn)行
- Runloop在沒(méi)有事件需要處理的時(shí)候烫堤,會(huì)使線程進(jìn)入休眠狀態(tài)荣赶,從而減少CPU的消耗,提高程序性能鸽斟。
- Runloop是一種消息機(jī)制的處理模式
二拔创、 Runloop的作用
- 保證程序持續(xù)運(yùn)行
- 喚醒線程,響應(yīng)程序的使用
- 在沒(méi)有事件需要處理的時(shí)候富蓄,使線程進(jìn)入休眠狀態(tài)剩燥,減少CPU的消耗。
三立倍、Runloop與線程的關(guān)系
- 線程與Runloop是一一對(duì)應(yīng)的關(guān)系
從代碼doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode))看出:
當(dāng)前runloop模式等于傳入的model灭红,或者是runloop模式是組合模式kCFRunLoopCommonModes,doit=YES口注,就會(huì)執(zhí)行变擒。
這也是為什么NSTimer加入到kCFRunLoopCommonModes模式下的runloop時(shí),不會(huì)因?yàn)轫?yè)面滑動(dòng)造成定時(shí)器延遲的原因了寝志。
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
// ...
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
}
if (!doit) prev = curr;
if (doit) {
if (prev) prev->_next = item;
if (curr == head) head = item;
if (curr == tail) tail = prev;
void (^block)(void) = curr->_block;
CFRelease(curr->_mode);
free(curr);
if (doit) {
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
did = true;
}
Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
if (head) {
tail->_next = rl->_blocks_head;
rl->_blocks_head = head;
if (!rl->_blocks_tail) rl->_blocks_tail = tail;
}
return did;
}
四娇斑、Runloop應(yīng)用
1. 使用Runloop來(lái)監(jiān)控程序的卡頓
參考文章:
深入理解RunLoop
iOS監(jiān)控卡頓
原理:
1.需要?jiǎng)?chuàng)建一個(gè)CFRunLoopObserverContext觀察者,然后將觀察者runLoopObserver添加到主線程 RunLoop的common模式下觀察
2.創(chuàng)建一個(gè)持續(xù)的子線程專門用來(lái)監(jiān)控主線程的RunLoop狀態(tài)
3.重點(diǎn)關(guān)注RunLoop 在進(jìn)入睡眠之前和喚醒后的兩個(gè) loop狀態(tài)定義的值分別是 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting材部,如果停留在這兩個(gè)狀態(tài)的連續(xù)超時(shí)50ms悠菜,則認(rèn)為是卡頓。
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
SeMonitorController *instrance = [SeMonitorController sharedInstance];
instrance->_activity = activity;
// 發(fā)送信號(hào)
dispatch_semaphore_t semaphore = instrance->_semaphore;
dispatch_semaphore_signal(semaphore);
}
- (void)registerObserver
{
CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
_observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities,
YES,
0,
&runLoopObserverCallBack,
&context);
CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
// 創(chuàng)建信號(hào)
_semaphore = dispatch_semaphore_create(0);
// 在子線程監(jiān)控時(shí)長(zhǎng)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (YES) {
// 假定連續(xù)5次超時(shí)50ms認(rèn)為卡頓(當(dāng)然也包含了單次超時(shí)250ms)
long st = dispatch_semaphore_wait(_semaphore, dispatch_time(DISPATCH_TIME_NOW, 0.05f*NSEC_PER_MSEC));
if (st != 0){
if (_activity==kCFRunLoopBeforeSources || _activity==kCFRunLoopAfterWaiting)
{
if (++_countTime < 5) continue;
NSLog(@"something lag");
}
}
_countTime = 0;
}
});
}
2. 使用CADisplayLink來(lái)監(jiān)控FPS
// 屏幕刷新頻率
// 將CADisplayLink添加到主Runloop中败富,來(lái)監(jiān)控主線的屏幕刷新頻率
_link = [CADisplayLink displayLinkWithTarget:weakself selector:@selector(tick:)];
[_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];