一、runLoop是什么
字面意思:運(yùn)行循環(huán),程序運(yùn)行過程中循環(huán)的處理事件
它的實(shí)際:實(shí)際是一個(gè)對(duì)象,這個(gè)對(duì)象提供一個(gè)入口函數(shù)胡诗,執(zhí)行這個(gè)入口函數(shù)后,程序會(huì)進(jìn)入一個(gè)do..while
循環(huán)淌友,循環(huán)的處理一些事情煌恢。
二、runLoop有什么用震庭?
2.1 如果沒有runLoop
症虑?
int main(int argc, char * argv[]){
@autoreleasepool {
NSLog(@“%s”, __func__);
}
return 0;
}
結(jié)果:程序執(zhí)行完就會(huì)退出。
2.2 如果有runLoop
?
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
結(jié)果:程序一直執(zhí)行沒有退出归薛。
三谍憔、runLoop在程序中的例子
3.1 觸摸
3.2 定時(shí)器
3.3
PerformSelector
3.4
GCD
異步操作回到主線程中總結(jié)
runLoop
基本作用:
1匪蝙、保持程序的持續(xù)運(yùn)行
2、處理App
中的各種事件(觸摸习贫、定時(shí)器逛球、PerformSelector
)
3、節(jié)省CPU
資源苫昌、提高程序性能:該做事的時(shí)候做事颤绕,該休息的時(shí)候休息。
四祟身、拓展關(guān)于PerformSelector
- (void)viewDidLoad {
[super viewDidLoad];
BOOL isNoFire = [self respondsToSelector:@selector(fire)];
if (isNoFire) {
[self performSelector:@selector(fire)];
}
}
- (void)fire {
NSLog(@"%s", __func__);
}
總結(jié):下面講講performSelector
調(diào)用方法和直接調(diào)用方法的區(qū)別
4.1奥务、performSelector
是運(yùn)行時(shí)系統(tǒng)負(fù)責(zé)去找方法的,在編譯時(shí)候不做任何校驗(yàn)袜硫;如果直接調(diào)用編譯是會(huì)自動(dòng)校驗(yàn)氯葬;
如果fire
不存在,那么直接調(diào)用:在編譯時(shí)候就能夠發(fā)現(xiàn)(借助Xcode
可以寫完就發(fā)現(xiàn))婉陷,但是使用performSelector
的話一定是在運(yùn)行時(shí)候才能發(fā)現(xiàn)(此時(shí)程序崩潰)帚称;
Cocoa
支持在運(yùn)行時(shí)向某個(gè)類添加方法,即方法編譯時(shí)不存在秽澳,但是運(yùn)行時(shí)候存在闯睹,這時(shí)候必然需要使用performSelector
去調(diào)用。所以有時(shí)候如果使用了performSelector
担神,為了程序的健壯性楼吃,會(huì)使用檢查方法respondsToSelector
。
4.2妄讯、直接調(diào)用方法時(shí)候孩锡,一定要在頭文件中聲明該方法的使用,也要將頭文件import
進(jìn)來捞挥。而使用performSelector
時(shí)候, 可以不用import
頭文件包含方法的對(duì)象忧吟,直接用performSelector
調(diào)用即可砌函。
4.3、performSelector
是在iOS
中的一種方法調(diào)用方式溜族。他可以向一個(gè)對(duì)象傳遞任何消息讹俊,而不需要在編譯的時(shí)候聲明這些方法。所以這也是runtime
的一種應(yīng)用方式煌抒。
所以performSelector
和直接調(diào)用方法的區(qū)別就在與runtime
仍劈。
直接調(diào)用編譯是會(huì)自動(dòng)校驗(yàn)。如果方法不存在寡壮,那么直接調(diào)用 在編譯時(shí)候就能夠發(fā)現(xiàn)贩疙,編譯器會(huì)直接報(bào)錯(cuò)讹弯。
但是使用performSelector
的話一定是在運(yùn)行時(shí)候才能發(fā)現(xiàn),如果此方法不存在就會(huì)崩潰这溅。所以如果使用performSelector
的話他就會(huì)有個(gè)最佳伴侶respondsToSelector:
來在運(yùn)行時(shí)判斷對(duì)象是否響應(yīng)此方法组民。
五、runLoop與線程的關(guān)系悲靴?
總結(jié):
5.1 線程與runLoop
是一一對(duì)應(yīng)的
5.2 線程創(chuàng)建的時(shí)候臭胜,并沒有創(chuàng)建runLoop
對(duì)象,runLoop
會(huì)在第一次獲取的時(shí)候自動(dòng)創(chuàng)建癞尚。
5.3 主線程默認(rèn)開啟了runLoop
耸三,子線程默認(rèn)沒有開啟。
源碼如下:
// 全局的Dictionary浇揩,key 是 pthread_t仪壮, value 是 CFRunLoopRef
static CFMutableDictionaryRef __CFRunLoops = NULL;
// 訪問 loopsDic 時(shí)的鎖
static CFLock_t loopsLock = CFLockInit;
// 獲取一個(gè) pthread 對(duì)應(yīng)的 RunLoop。
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
// 第一次進(jìn)入時(shí)临燃,初始化全局Dic睛驳,并先為主線程創(chuàng)建一個(gè) RunLoop。
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
}
// 直接從 Dictionary 里獲取膜廊。
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
// 取不到時(shí)乏沸,創(chuàng)建一個(gè)
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
return loop;
}
從上面的代碼可以看出,線程和 runLoop
之間是一一對(duì)應(yīng)的爪瓜,其關(guān)系是保存在一個(gè)全局的 Dictionary
里蹬跃。線程剛創(chuàng)建時(shí)并沒有 RunLoop
,如果你不主動(dòng)獲取铆铆,那它一直都不會(huì)有蝶缀。RunLoop
的創(chuàng)建是發(fā)生在第一次獲取時(shí),RunLoop
的銷毀是發(fā)生在線程結(jié)束時(shí)薄货。你只能在一個(gè)線程的內(nèi)部獲取其 RunLoop
(主線程除外)翁都。
六、runLoop執(zhí)行過程谅猾?
//
// RunLoop內(nèi)部執(zhí)行過程.c
// TZRunloop001-初識(shí)
//
// Created by hzg on 2018/8/25.
// Copyright ? 2018年 tz. All rights reserved.
//
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);
/// 首先根據(jù)modeName找到對(duì)應(yīng)mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
/// 通知 Observers: RunLoop 即將進(jìn)入 loop柄慰。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
/// 內(nèi)部函數(shù),進(jìn)入loop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
/// 通知 Observers: RunLoop 即將退出税娜。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
/// 核心函數(shù)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
/// 通知 Observers: 即將處理timer事件
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
/// 通知 Observers: 即將處理Source事件
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
/// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
/// 處理sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
/// 處理sources0返回為YES
if (sourceHandledThisLoop) {
/// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
}
/// 判斷有無端口消息(Source1)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
/// 處理消息
goto handle_msg;
}
/// 通知 Observers: 即將進(jìn)入休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
/// 等待被喚醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
// user callouts now OK again
__CFRunLoopUnsetSleeping(rl);
/// 通知 Observers: 被喚醒坐搔,結(jié)束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:
if (被Timer喚醒) {
/// 處理Timers
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
} else if (被GCD喚醒) {
/// 處理gcd
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else if (被Source1喚醒) {
/// 被Source1喚醒敬矩,處理Source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
/// 處理block
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}
// main dispatch queue
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
// __CFRunLoopDoObservers
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
// __CFRunLoopDoBlocks
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
// __CFRunLoopDoSources0
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
// __CFRunLoopDoSource1
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
// __CFRunLoopDoTimers
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
七概行、休眠的原理?
RunLoop
實(shí)現(xiàn)休眠的原理, 真正的原因是:
- 調(diào)用了內(nèi)核的
API
(mach_msg
), 進(jìn)入內(nèi)核態(tài)弧岳,由內(nèi)核來將線程置于休眠 -
有消息凳忙,就喚醒線程业踏,回到用戶態(tài),來處理消息.
八消略、GCD Timer與NSTimer區(qū)別堡称?
8.1、NSTimer不準(zhǔn)時(shí)的原因艺演?
1.
runLoop
循環(huán)處理的這個(gè)時(shí)間
2.受runLoop
模式的影響
8.2却紧、GCD Timer與NSTimer不同的原因?
- 都是源胎撤,一個(gè)是
runLoop
的源晓殊,一個(gè)Dispatch
的源GCD timer
不需要加入mode
代碼如下
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self gcdTimerTest];
}
- (void) gcdTimerTest {
// 隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue(); // 主線程隊(duì)列,會(huì)受到runLoop影響
// dispatch_queue_t queue = dispatch_queue_create("timer_serial_label", DISPATCH_QUEUE_SERIAL); // 子線程隊(duì)列伤提,不會(huì)受到runLoop影響
// 創(chuàng)建定時(shí)器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 設(shè)置定時(shí)的開始時(shí)間巫俺、間隔時(shí)間
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0), 1*NSEC_PER_SEC, 0);
// 設(shè)置定時(shí)器回調(diào)
dispatch_source_set_event_handler(timer, ^{
NSLog(@"你好");
});
// 啟動(dòng)定時(shí)器,默認(rèn)是關(guān)閉的
dispatch_resume(timer);
self.timer = timer;
}
總結(jié):GCD Timer總結(jié)肿男?
1.
GCD Timer
精度高
2.GCD Timer
主線程執(zhí)行會(huì)受runLoop
影響介汹,子線程不受影響
3.GCD Timer
不受模式切換的影響