1.探索
首先通過po [NSRunloop currentRunloop]
在控制臺可以看到以下打印信息太闺,發(fā)現(xiàn)runloop與autoreleasepool有關系
- 我們可以推測宠页,在觸發(fā)obsever監(jiān)聽的時候葵孤,會處理autoreleasepool
- order表示runloop處理的優(yōu)先級
- 我們通過activities = 0xa0,可以監(jiān)聽autoreleasepool的處理
2.監(jiān)聽autoreleasepool
char autoreleaseActive = 0xa0;
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), autoreleaseActive, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"_wrapRunLoopWithAutoreleasePoolHandler");
});
- 通過實踐鼻由,在一次runloop的周期中大概會執(zhí)行5次暇榴。
-
添加一個信號斷點
- 發(fā)現(xiàn)在閑置狀態(tài)(beforeWating)會不時進入這個斷點
3.從runloop源碼探究
- 雖然autoreleasepool的處理一直被觸發(fā),但是并沒有喚醒休眠狀態(tài)的runloop
4. autoreleasepool源碼
提問:arc有必要使用autoreleasepool嗎蕉世?
- 在mrc的時代蔼紧,往往創(chuàng)建一個autoreleasepool對象
- arc則使用
@ autoreleasepool
提問:arc下使用@ autoreleasepool 的意義?
- 首先執(zhí)行下面兩段代碼作為對比
for (int i = 0; i < 1024*1024*30; i++) {
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"0123456789"];
};
}
for (int i = 0; i < 1024*1024*30; i++) {
NSString *str = [NSString stringWithFormat:@"0123456789"];
}
- 結論:
第一段代碼在會對內(nèi)存進行優(yōu)化狠轻,釋放速度快奸例;
第二段代碼造成內(nèi)存大量堆積,釋放緩慢 - 使用場景:當程序有大量中間臨時變量產(chǎn)生時,避免內(nèi)存使用峰值過高查吊,及時釋放內(nèi)存的場景
通過clang解讀@autoreleasepool源碼
- 命令
clang -rewrite-objc
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"0123456789"];
};
約等于
創(chuàng)建了一個autorelease對象 push autorelease
NSString *str = [NSString stringWithFormat:@"0123456789"];
釋放掉autorelease(pop ~) clang
- 但是從clang出的源碼中我們看不出釋放谐区,是因為autorelease的釋放使用c++中的析構
5. autoreleasepool底層運行原理
- AutoreleasePoolPage實例
magic_t const magic; //用來校驗 AutoreleasePoolPage 的結構是否完整
id *next;//指向最新添加的 autoreleased 對象的下一個位置,初始化時指向 begin()
pthread_t const thread;//指向當前線程
AutoreleasePoolPage * const parent;// 指向父結點逻卖,第一個結點的 parent 值為 nil
AutoreleasePoolPage *child;//指向父結點宋列,第一個結點的 parent 值為 nil
uint32_t const depth;//鏈表的深度,節(jié)點個數(shù)
uint32_t hiwat;//high water mark 數(shù)據(jù)容納的一個上限
PAGE_MAX_SIZE;//size大小為4096,虛擬內(nèi)存每個扇區(qū)4096個字節(jié)评也,4K對齊的說法
COUNT;//一個page里的對象數(shù)
POOL_BOUNDARY;//邊界對象炼杖,以前為POOL_SENTINEL哨兵對象
(AutoreleasePool并沒有單獨的結構,而是由若干個AutoreleasePoolPage以雙向鏈表的形式組成)
- 當我們添加一個需要被Autorelease的對象
string1
magic_t const magic;
id *next;
NSString *string1;
pthread_t const thread;
AutoreleasePoolPage * const parent;的 parent 值為 nil
AutoreleasePoolPage *child;值為 nil
uint32_t const depth;
uint32_t hiwat;
PAGE_MAX_SIZE;
COUNT;
POOL_BOUNDARY;
- 再添加
string2
盗迟、string3
內(nèi)部會變成
magic_t const magic;
id *next;
NSString *string3;
NSString *string2;
NSString *string1;
pthread_t const thread;
AutoreleasePoolPage * const parent;的 parent 值為 nil
AutoreleasePoolPage *child;值為 nil
uint32_t const depth;
uint32_t hiwat;
PAGE_MAX_SIZE;
COUNT;
POOL_BOUNDARY;
這個操作是由id *next
實現(xiàn)的坤邪,它在有新的對象加入AutoreleasePoolPage時,會自動指向下一個地址
- 當添加到上限時,新的對象就會加入到下一個page罚缕,所以構成了雙向鏈表的結構
magic_t const magic; magic_t const magic;
id *next; id *next;
..... NSString *string49;
NSString *...; NSString *string50;
NSString *...; pthread_t const thread;
NSString *string3; ......
NSString *string2; ......
NSString *string1;
pthread_t const thread;
AutoreleasePoolPage * const
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
PAGE_MAX_SIZE;
COUNT;
POOL_BOUNDARY;
Page 1 page2
總結
- AutoreleasePool并沒有單獨的結構罩扇,而是由若干個AutoreleasePoolPage以雙向鏈表的形式組成
- AutoreleasePoolPage每個對象會開辟4096字節(jié)內(nèi)存(也就是虛擬內(nèi)存一頁的大小)怕磨,除了上面的實例變量所占空間,剩下的空間全部用來儲存autorelease對象的地址
- iOS里的TaggedPointer不適用autorelesepool
- NSAutoreleasePool可以創(chuàng)建一個autorelease pool消约,但該對象本身也需要被釋放 drain
- 在ARC下肠鲫,不能使用上述方式調(diào)用autorelease,而應當使用@autoreleasepool{}
- 對于不同線程或粮,應當創(chuàng)建自己的autorelease pool导饲。如果應用長期存在,應該定期drain和創(chuàng)建新的autorelease pool氯材。
- runloop 與 AutoreleasePool 是協(xié)同合作關系
- AutoreleasePool 與 runloop 與線程是一一對應的關系
- AutoreleasePool 在 runloop 在開始時被push渣锦,在runloop休眠時(beforewaiting狀態(tài))pop