-
?? 上一節(jié) ,詳細(xì)介紹了
weak、strong、強(qiáng)引用的解決方案。本節(jié),我們將介紹:
- autorelease自動(dòng)釋放池
- runloop
準(zhǔn)備工作:
- 可編譯的
objc4-781源碼: http://www.itdecent.cn/p/45dc31d91000- runloop源碼: https://opensource.apple.com/tarballs/CF/
1.autorelease自動(dòng)釋放池
-
autorelease自動(dòng)釋放池:自動(dòng)管理作用域內(nèi)對(duì)象引用計(jì)數(shù)的池子。
面試題1:
臨時(shí)變量什么時(shí)候釋放?
面試題2:簡(jiǎn)述自動(dòng)釋放池原理
面試題3:自動(dòng)釋放池能否嵌套使用?
1.1 初探autorelease
-
APP的入口函數(shù)main,包含了@autoreleasepool:
int main(int argc, const char * argv[]) {
@autoreleasepool {
}
return 0;
}
- 使用
clang將main.m編譯后輸出main.cpp,在cpp文件中,可以看到:
image.png

@autoreleasepool在編譯期轉(zhuǎn)化為了__AtAutoreleasePool結(jié)構(gòu)體。__AtAutoreleasePool的構(gòu)造函數(shù)創(chuàng)建了自動(dòng)釋放池對(duì)象__AtAutoreleasePool的析構(gòu)函數(shù)釋放了自動(dòng)釋放池對(duì)象
- 仿
__AtAutoreleasePool實(shí)現(xiàn)一個(gè)構(gòu)造和析構(gòu)函數(shù),觀察生命周期:
image.png
利用
結(jié)構(gòu)體的構(gòu)造和析構(gòu)函數(shù),有效的匹配作用域。
1.2 源碼分析
- 定位源碼: (
libobjc庫(kù))
在
main.m文件的@autoreleasepool處加上斷點(diǎn),打開(kāi)匯編模式,運(yùn)行代碼:
image.png加入
objc_autoreleasePoolPush符號(hào)斷點(diǎn),運(yùn)行代碼,發(fā)現(xiàn)源碼在libobjc庫(kù)
image.png
- 打開(kāi)
objc4源碼,搜索objc_autoreleasePoolPush:
image.png
1.2.1 自動(dòng)釋放池結(jié)構(gòu)


1.2.2 push自動(dòng)釋放池

1.2.3 pop自動(dòng)釋放池

1.3 代碼驗(yàn)證:
必須在
MRC環(huán)境下,才可以使用autorelease
image.png
#import <objc/runtime.h>
#import <malloc/malloc.h>
// 聲明外部實(shí)現(xiàn)
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
for (int i = 0 ; i<505; i++) {
NSObject * objc = [[NSObject alloc] autorelease];
}
// 打印自動(dòng)釋放池的結(jié)構(gòu)信息
_objc_autoreleasePoolPrint();
}
return 0;
}
-
打印結(jié)果:
image.png
image.png 數(shù)據(jù)較多,只截取了
第一頁(yè)開(kāi)頭和第二頁(yè)數(shù)據(jù)
1.4 autorelease的嵌套
#import <objc/runtime.h>
#import <malloc/malloc.h>
// 聲明外部實(shí)現(xiàn)
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
for (int i = 0 ; i<5; i++) {
NSObject * objc = [[NSObject alloc] autorelease];
}
@autoreleasepool {
for (int i = 0 ; i<3; i++) {
NSObject * objc = [[NSObject alloc] autorelease];
}
// 打印自動(dòng)釋放池的結(jié)構(gòu)信息
_objc_autoreleasePoolPrint();
printf("\n-----------------------------\n");
}
// 打印自動(dòng)釋放池的結(jié)構(gòu)信息
_objc_autoreleasePoolPrint();
}
return 0;
}

autoRelease的嵌套,并沒(méi)有在結(jié)構(gòu)上進(jìn)行嵌套。而是利用哨兵的作用,直接多插入一個(gè)哨兵。- 因?yàn)槊看?code>遇到哨兵(
pop出棧時(shí)),都表示一個(gè)autorelease可釋放
2. runloop
【前言】
關(guān)于runloop,我看了一些資料,越看越把我看暈。 停下來(lái)稍微理一下。
(ps:runloop版本號(hào)是我虛構(gòu)的,輔助理解)
【使命】
runloop的官方文檔是在thread線程板塊中,他只是線程的一個(gè)輔助方式。很簡(jiǎn)單的理解:一個(gè)
線程,我們創(chuàng)建后,執(zhí)行完任務(wù),它就釋放了。 那每次使用,我都這樣創(chuàng)建->執(zhí)行->釋放,豈不是很麻煩?我也不知道我啥時(shí)候會(huì)用到,我就
希望有一個(gè)線程,在我需要在這個(gè)線程執(zhí)行任務(wù)的時(shí)候,直接把任務(wù)丟過(guò)去就可以了。很開(kāi)心,runloop滿足你。【實(shí)現(xiàn)】
runloop,顧名思義,就是一個(gè)(run)運(yùn)行(loop)循環(huán)。它的作用上面說(shuō)了,就是讓線程一直保持可用狀態(tài)(?;?/code>),如何保持一直在線?
第一想法是,給個(gè)do-while(1)循環(huán),循環(huán)內(nèi)可以接受外部函數(shù),我們每次要執(zhí)行任務(wù)時(shí),給他一個(gè)函數(shù)就可以了。聰明!runloop1.0版本已經(jīng)被你開(kāi)發(fā)了??。【小結(jié)】
runloop,本身就是一個(gè)函數(shù),函數(shù)內(nèi)創(chuàng)建do-while循環(huán),一直持有著當(dāng)前線程(在哪個(gè)線程調(diào)用它,它就一直持有著哪個(gè)線程)。【優(yōu)化】按照上面使用
do-while(1)循環(huán),我們會(huì)發(fā)現(xiàn)cpu在激增,因?yàn)樗?code>一直在運(yùn)行。那有人就有想法了,能不能我需要的時(shí)候它就運(yùn)行,我不用的時(shí)候,它就休息,不要占用我的cpu資源。我真不想要這個(gè)線程的時(shí)候,線程和循環(huán)都給我銷毀。可以不可以呀?
要求挺多呀,但挺合理的。?? 于是runloop2.0版本滿足你??。【第一個(gè)要求:支持
銷毀】
runloop給do-while加上條件,你不要了就把這條件設(shè)置為不滿足就OK啦。【第二個(gè)要求:支持
休眠和喚醒】首先,
runloop得知道它到底還有沒(méi)有沒(méi)干完的活。怎么界定呢? 簡(jiǎn)單,runloop列個(gè)業(yè)務(wù)清單,打今兒起,我就只接這幾種業(yè)務(wù),做完了會(huì)回調(diào)你。(像不像去抽血,1,2,3號(hào)抽血,請(qǐng)?jiān)?,5,6窗口等結(jié)果。血液檢測(cè)完了,你就可以拿到結(jié)果了 ??)。 如果所有業(yè)務(wù)都處理完了,就進(jìn)入休眠狀態(tài)(沒(méi)活了可以玩會(huì)手機(jī),休息下)那
啥時(shí)候喚醒?怎么被喚醒呢?
runloop直接使用系統(tǒng)內(nèi)核的mach port的消息機(jī)制mach_msg(),當(dāng)接收業(yè)務(wù)時(shí),系統(tǒng)會(huì)(通過(guò)source1)直接喚醒runloop,去執(zhí)行現(xiàn)在當(dāng)前接收到的任務(wù)。
每一次循環(huán),都會(huì)查詢活有沒(méi)有干完,有沒(méi)有其他活在排隊(duì)。沒(méi)有了就休息。收到系統(tǒng)消息就接著干活。總之,目前我就是這么
理解runloop的,總結(jié)一下:
- 作用:為
線程?;?/code>(所以一個(gè)線程一個(gè)runloop,一一對(duì)應(yīng))- 實(shí)現(xiàn):
線程內(nèi)的一個(gè)函數(shù),弄個(gè)do-while循環(huán)讓這個(gè)線程一直在線??梢?code>處理幾類事務(wù)并回調(diào)處理結(jié)果。支持休眠和被喚醒,也支持銷毀。
相關(guān)鏈接:
?? RunLoop 官方文檔
?? 邏輯教育kody老師的公開(kāi)課
?? ibireme大神的Runloop分析
?? RunLoop 源碼閱讀
- 至此,我想你
內(nèi)心對(duì)runloop已經(jīng)有了一個(gè)大體認(rèn)知。
(一開(kāi)始就一頭扎進(jìn)源碼的我,可沒(méi)這么幸運(yùn)??)
- 現(xiàn)在,我們來(lái)
正式了解runloop:
2.1 runloop是什么
runloop:
- 使用一個(gè)
循環(huán),保持程序的持續(xù)運(yùn)行;
一個(gè)線程對(duì)應(yīng)一個(gè)runloop,負(fù)責(zé)處理APP中各種事件(觸摸、定時(shí)器、performSelector)
節(jié)省cpu資源。(無(wú)任務(wù)時(shí)自動(dòng)休眠,被喚醒后繼續(xù)工作)
-
經(jīng)典runloop流程圖:
image.png
2.1.1 runloop的循環(huán)
-
簡(jiǎn)單循環(huán)案例,會(huì)占用cpu。
image.png -
runloop的循環(huán),閑時(shí)不會(huì)占用cpu。
(app啟動(dòng)就會(huì)啟動(dòng)主線程,主線程內(nèi)就維持著一個(gè)runloop,一直給程序?;?/code>。)
image.png -
我們下載runloop源碼,將
CFRunLoop.c和CFRunLoop.h文件拖入demo文件夾,搜索void CFRunloopRun:
image.png 發(fā)現(xiàn)
runloop的[do-while]循環(huán)在stop或finished時(shí),會(huì)結(jié)束。
ps:
驗(yàn)證了runloop1.0到2.0的過(guò)渡??
2.2 runloop線程保活
上面我們說(shuō)了,一個(gè)runloop對(duì)應(yīng)一個(gè)線程,作用就是線程?;?/code>:
2.2.1 原始線程(用完即銷毀):
//MARK: - HTThread
// 繼承NSThread,為了打印dealloc - 線程釋放
@interface HTThread : NSThread
@end
@implementation HTThread
-(void)dealloc{ NSLog(@"%@ %s",[HTThread currentThread].name,__func__); }
@end
//MARK: - ViewController
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 調(diào)用完,直接銷毀
HTThread * thread = [[HTThread alloc]initWithTarget:self selector:@selector(threadTest) object:nil];
thread.name = @"ht_Thread";
[thread start];
}
- (void)threadTest {
NSLog(@"%@ %s",[NSThread currentThread].name, __func__);
}
@end

- 可以看到,名為
ht_thread的線程,執(zhí)行完任務(wù)(threadTest函數(shù))后,就被銷毀了。
2.2.2 runloop線程?;睿?/h5>
@interface HTThread : NSThread
@end
@implementation HTThread
-(void)dealloc{ NSLog(@"%@ %s",[HTThread currentThread].name,__func__); }
@end
//MARK: -ViewController
@interface ViewController ()
@property(nonatomic, strong) HTThread * thread;
@property(nonatomic, strong) NSRunLoop * runloop; // 常駐線程
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createThread];
}
- (void)createThread {
_thread = [[HTThread alloc]initWithTarget:self selector:@selector(threadTest) object:nil];
_thread.name = @"ht_Thread";
[_thread start];
}
- (void)threadTest {
NSLog(@"%@ %s",[NSThread currentThread].name, __func__);
// @autoreleasepool 對(duì)子線程中的臨時(shí)變量做優(yōu)化管理。更高效利用空間
@autoreleasepool {
// 使用runloop對(duì)當(dāng)前線程保活(當(dāng)前`threadTest`函數(shù)是在`ht_Thread`線程內(nèi)執(zhí)行)
_runloop = [NSRunLoop currentRunLoop];
[_runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
// 處理主線程,其他線程都需要手動(dòng)開(kāi)啟runloop (run內(nèi)綁定了線程與runloop的關(guān)系)
[_runloop run];
}
// runloop沒(méi)被釋放,就到不來(lái)這一行。threadTest函數(shù)也一直不會(huì)結(jié)束
NSLog(@"runloop釋放了 %@ %s",[NSThread currentThread].name, __func__);
}
- (void)threadTask {
NSLog(@"%@ %s",[NSThread currentThread].name, __func__);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 點(diǎn)擊,讓_thread線程執(zhí)行任務(wù)(`threadTask`函數(shù))
// 如果疑惑_thread是被self強(qiáng)持有,本身就可執(zhí)行的話。 可手動(dòng)注釋`@autoreleasepool`內(nèi)部代碼。再點(diǎn)擊檢驗(yàn)。會(huì)發(fā)現(xiàn)崩潰了。
// 因?yàn)殡m然_thread在,但是沒(méi)法讓它為你工作,runloop是可以幫你管理_thread并讓它為你工作的。
[self performSelector:@selector(threadTask) onThread:_thread withObject:nil waitUntilDone:YES];
}
@end
-
runloop成功的實(shí)現(xiàn)了線程?;?/code>。(我想用的時(shí)候,都可以用)
image.png
2.3 runloop的讀取
-
runloop的讀取,支持2種方式:
主線程獲取(CFRunLoopGetMain) 和 當(dāng)前線程獲取(CFRunLoopGetCurre)。內(nèi)部調(diào)用_CFRunLoopGet0函數(shù)。
image.png
總結(jié):
-
主線程是static全局唯一的,第一次獲取時(shí)創(chuàng)建。
-
線程不存在,默認(rèn)使用主線程,并返回主線程的runloop
-
首次訪問(wèn),會(huì)創(chuàng)建全局唯一的__CFRunLoops字典。key為線程,value為runloop。
(線程與runloop是一一對(duì)應(yīng))
- 每次
優(yōu)先從__CFRunLoops字典中,通過(guò)key(線程),獲取value(runloop)。
- 如果
runloop 不存在,就創(chuàng)建線程對(duì)應(yīng)的runloop,并更新__CFRunLoops字典對(duì)應(yīng)值。
- 更新
TSD(線程私有存儲(chǔ)),記錄runloop。
- 返回
runloop
面試題: runLoop與線程的關(guān)系
一一對(duì)應(yīng)關(guān)系。由全局Runloop字典進(jìn)行記錄,其中key為線程,value為runloop。
2.4 runloop的創(chuàng)建
image.png
總結(jié):
- 以
__CFRunLoop為模板,創(chuàng)建Runloop結(jié)構(gòu)體對(duì)象
-
屬性的初始化賦值
-
Mode的獲?。?
- 如果通過(guò)
__kCFRunLoopModeTypeID讀取到Modes,并且 Modes中存在kCFRunLoopDefaultMode,就直接返回找到的Mode。
- 否則,
創(chuàng)建一個(gè)Mode,加入modes中。返回Mode。
拓展:
-
runLoop本質(zhì)是__CFRunLoop格式的結(jié)構(gòu)體。
記錄線程鎖,port喚醒端口,所在線程、所有標(biāo)記為Common的Mode、加入CommonMode的item事務(wù)、當(dāng)前Mode和所有Mode
image.png
- 理解
commonModes和modes:
image.png
2.5 runloop的運(yùn)行原理
image.png
- 看完源碼后,
runloop的運(yùn)行周期,喚醒方式都十分清晰了?,F(xiàn)在奉上經(jīng)典的Runloop流程圖:
image.png
補(bǔ)充說(shuō)明:
【最外層流程】
kCFRunLoopEntry進(jìn)入循環(huán) (發(fā)通知)
-> __CFRunLoopRun 運(yùn)行循環(huán)
-> kCFRunLoopExit退出循環(huán)(發(fā)通知)
【循環(huán)內(nèi)部】
kCFRunLoopBeforeTimers即將處理Timer (發(fā)通知)
-> kCFRunLoopBeforeSources 即將處理Sources0 (發(fā)通知)
(__CFRunLoopDoBlocks 處理Blocks)
-> __CFRunLoopDoSources0 處理Sources0
(__CFRunLoopDoBlocks 處理Blocks)
-> __CFRunLoopServiceMachPort : 監(jiān)聽(tīng)Port端口消息(source1),有消息就跳轉(zhuǎn)handle_msg
-> kCFRunLoopBeforeWaiting: 將進(jìn)入休眠 (發(fā)通知)
進(jìn)入休眠,等待喚醒 (內(nèi)部的Timer到期、gcd都可喚醒)
-> 線程被喚醒, (發(fā)通知)
3.【handle_msg】處理消息:
- 被Timers喚醒(
CFRUNLOOP_WAKEUP_FOR_TIMER): __CFRunLoopDoTimers (發(fā)通知)
- 被gcd喚醒(
CFRUNLOOP_WAKEUP_FOR_DISPATCH):__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ (發(fā)通知)
- 被source喚醒(
CFRUNLOOP_WAKEUP_FOR_SOURCE): __CFRunLoopDoSource1
(__CFRunLoopDoBlocks 處理Blocks)
- 檢查
stop或finish
Timer、dispatch、source等回調(diào)函數(shù):
// 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__
在執(zhí)行回調(diào)Block前,我們可以在堆棧中看到上述回調(diào)函數(shù)。
- 回調(diào)函數(shù)檢驗(yàn):
(每次觸發(fā)TouchBegin時(shí),所在線程的runloop都會(huì)調(diào)用__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__函數(shù))
image.png
@interface HTThread : NSThread
@end
@implementation HTThread
-(void)dealloc{ NSLog(@"%@ %s",[HTThread currentThread].name,__func__); }
@end
//MARK: -ViewController
@interface ViewController ()
@property(nonatomic, strong) HTThread * thread;
@property(nonatomic, strong) NSRunLoop * runloop; // 常駐線程
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createThread];
}
- (void)createThread {
_thread = [[HTThread alloc]initWithTarget:self selector:@selector(threadTest) object:nil];
_thread.name = @"ht_Thread";
[_thread start];
}
- (void)threadTest {
NSLog(@"%@ %s",[NSThread currentThread].name, __func__);
// @autoreleasepool 對(duì)子線程中的臨時(shí)變量做優(yōu)化管理。更高效利用空間
@autoreleasepool {
// 使用runloop對(duì)當(dāng)前線程保活(當(dāng)前`threadTest`函數(shù)是在`ht_Thread`線程內(nèi)執(zhí)行)
_runloop = [NSRunLoop currentRunLoop];
[_runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
// 處理主線程,其他線程都需要手動(dòng)開(kāi)啟runloop (run內(nèi)綁定了線程與runloop的關(guān)系)
[_runloop run];
}
// runloop沒(méi)被釋放,就到不來(lái)這一行。threadTest函數(shù)也一直不會(huì)結(jié)束
NSLog(@"runloop釋放了 %@ %s",[NSThread currentThread].name, __func__);
}
- (void)threadTask {
NSLog(@"%@ %s",[NSThread currentThread].name, __func__);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 點(diǎn)擊,讓_thread線程執(zhí)行任務(wù)(`threadTask`函數(shù))
// 如果疑惑_thread是被self強(qiáng)持有,本身就可執(zhí)行的話。 可手動(dòng)注釋`@autoreleasepool`內(nèi)部代碼。再點(diǎn)擊檢驗(yàn)。會(huì)發(fā)現(xiàn)崩潰了。
// 因?yàn)殡m然_thread在,但是沒(méi)法讓它為你工作,runloop是可以幫你管理_thread并讓它為你工作的。
[self performSelector:@selector(threadTask) onThread:_thread withObject:nil waitUntilDone:YES];
}
@end
runloop成功的實(shí)現(xiàn)了線程?;?/code>。(我想用的時(shí)候,都可以用)
image.png
runloop的讀取,支持2種方式:主線程獲取(CFRunLoopGetMain) 和 當(dāng)前線程獲取(CFRunLoopGetCurre)。內(nèi)部調(diào)用_CFRunLoopGet0函數(shù)。
總結(jié):
-
主線程是static全局唯一的,第一次獲取時(shí)創(chuàng)建。 -
線程不存在,默認(rèn)使用主線程,并返回主線程的runloop -
首次訪問(wèn),會(huì)創(chuàng)建全局唯一的__CFRunLoops字典。key為線程,value為runloop。
(線程與runloop是一一對(duì)應(yīng)) - 每次
優(yōu)先從__CFRunLoops字典中,通過(guò)key(線程),獲取value(runloop)。 - 如果
runloop不存在,就創(chuàng)建線程對(duì)應(yīng)的runloop,并更新__CFRunLoops字典對(duì)應(yīng)值。 - 更新
TSD(線程私有存儲(chǔ)),記錄runloop。 - 返回
runloop
面試題: runLoop與線程的關(guān)系
一一對(duì)應(yīng)關(guān)系。由全局Runloop字典進(jìn)行記錄,其中key為線程,value為runloop。

總結(jié):
- 以
__CFRunLoop為模板,創(chuàng)建Runloop結(jié)構(gòu)體對(duì)象 -
屬性的初始化賦值 -
Mode的獲?。?- 如果通過(guò)
__kCFRunLoopModeTypeID讀取到Modes,并且Modes中存在kCFRunLoopDefaultMode,就直接返回找到的Mode。 - 否則,
創(chuàng)建一個(gè)Mode,加入modes中。返回Mode。
- 如果通過(guò)
拓展:
-
runLoop本質(zhì)是__CFRunLoop格式的結(jié)構(gòu)體。記錄
線程鎖,port喚醒端口,所在線程、所有標(biāo)記為Common的Mode、加入CommonMode的item事務(wù)、當(dāng)前Mode和所有Mode

- 理解
commonModes和modes:
image.png

runloop的運(yùn)行周期,喚醒方式都十分清晰了?,F(xiàn)在奉上經(jīng)典的Runloop流程圖:
補(bǔ)充說(shuō)明:
【最外層流程】
kCFRunLoopEntry進(jìn)入循環(huán) (發(fā)通知)
->__CFRunLoopRun運(yùn)行循環(huán)
->kCFRunLoopExit退出循環(huán)(發(fā)通知)【循環(huán)內(nèi)部】
kCFRunLoopBeforeTimers即將處理Timer(發(fā)通知)
->kCFRunLoopBeforeSources即將處理Sources0(發(fā)通知)
(__CFRunLoopDoBlocks處理Blocks)
->__CFRunLoopDoSources0處理Sources0
(__CFRunLoopDoBlocks處理Blocks)
->__CFRunLoopServiceMachPort: 監(jiān)聽(tīng)Port端口消息(source1),有消息就跳轉(zhuǎn)handle_msg
->kCFRunLoopBeforeWaiting: 將進(jìn)入休眠 (發(fā)通知)
進(jìn)入休眠,等待喚醒 (內(nèi)部的Timer到期、gcd都可喚醒)
->線程被喚醒, (發(fā)通知)
3.【handle_msg】處理消息:
- 被Timers喚醒(
CFRUNLOOP_WAKEUP_FOR_TIMER):__CFRunLoopDoTimers(發(fā)通知) - 被gcd喚醒(
CFRUNLOOP_WAKEUP_FOR_DISPATCH):__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(發(fā)通知) - 被source喚醒(
CFRUNLOOP_WAKEUP_FOR_SOURCE):__CFRunLoopDoSource1
(__CFRunLoopDoBlocks處理Blocks) - 檢查
stop或finish
Timer、dispatch、source等回調(diào)函數(shù):
// 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__
在執(zhí)行回調(diào)Block前,我們可以在堆棧中看到上述回調(diào)函數(shù)。
(每次
觸發(fā)TouchBegin時(shí),所在線程的runloop都會(huì)調(diào)用__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__函數(shù))
至此,完成了runloop的基礎(chǔ)探索。













