前言
關(guān)于AutoreleasePool的實(shí)現(xiàn)原理,有很多很多優(yōu)秀的博客(都是大神們無私的奉獻(xiàn)),他們都對其進(jìn)行了詳細(xì)的介紹,我也是看這些文章配合runtime源碼進(jìn)行學(xué)習(xí)的。文章只是做了一些干練的總結(jié),方便自己或者他人復(fù)習(xí),具體的細(xì)節(jié)以及分析就不寫了(大神們寫得太好了),想要學(xué)習(xí)的移步大神們的博客。(ps: 雖然這些博客中的runtime部分源碼已經(jīng)修改,但是其基本邏輯還是沒有變動的,runtime可調(diào)式工程在此下載)
大神的文章
黑幕背后的Autorelease - sunnyxx大神
Objective-C Autorelease Pool 的實(shí)現(xiàn)原理 - 雷純鋒的技術(shù)博客
@autoreleasepool的實(shí)質(zhì)
通過clang -rewrite-objc指令可以將:
int main (int argc, char * argv[]) {
@autoreleasepool {
}
}
轉(zhuǎn)換成:
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;
};
#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
int main (int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
}
}
可以看到編譯器實(shí)質(zhì)用一個(gè)棧上的c++對象來替換@autoreleasepool{};并在對象的構(gòu)造中調(diào)用了:objc_autoreleasePoolPush();在析構(gòu)中調(diào)用了:objc_autoreleasePoolPop(atautoreleasepoolobj)。
其實(shí)際都是調(diào)用runtime中c++類AutoreleasePoolPage的push和pop方法。
AutoreleasePoolPage的定義
看一下AutoreleasePoolPage中定義的成員變量:
class AutoreleasePoolPage
{
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
各個(gè)變量基本上都能見其名知其意。
-
autorelease類型的對象都是通過AutoreleasePoolPage管理的。 - 在
masOS中每個(gè)page的大小是4096個(gè)字節(jié)。
對于page的大小,可以從源碼中看到:
static void * operator new(size_t size) {
return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
}
static void operator delete(void * p) {
return free(p);
}
其重寫了運(yùn)算new和delete,而SIZE在macOS下定義為4096。
- 內(nèi)存從低址值存依次放著各個(gè)成員變量(56個(gè)字節(jié));然后存放
POOL_BOUNDARY和autorelease類型對象的指針。 - 其中
next是指向下一個(gè)autorelease類型對象的指針該存放的位置。 - 每個(gè)
AutoreleasePoolPage對象通過parent和child來連接(雙鏈表)。 - 在
autoreleasepool中嵌套autoreleasepool實(shí)際上是在page中push一個(gè)POOL_BOUNDARY(nil)。
當(dāng)調(diào)用對象的autorelease方法時(shí),該對象的指針會被存放到page中,而當(dāng)page進(jìn)行pop操作時(shí),會根據(jù)傳入的POOL_BOUNDARY(nil)指針的地址來釋放大于此地址的page中的對象。
結(jié)尾
紙上得來終覺淺,絕知此事要躬行。