蘋果內(nèi)存管理方案主要為MRC
和ARC
TaggedPointer
:小對象類型困檩,NSDate虱而、NSNumber等
NonpointerIsa
:非指針型isa
散列表
:引用計數(shù)表尾膊,弱引用表
TaggedPointer
上述代碼中挺尾,兩個name
的類型其實是不一樣的变勇。
name
是NSTaggedPointerString
肥隆,他是經(jīng)過Xcode
優(yōu)化的既荚,存放在常量區(qū)。
這里的
name
是NSCFString
巷屿,存放在堆區(qū)固以。
接下來看一下源碼,看看這兩個類型有什么區(qū)別嘱巾。
我們知道setter
方法憨琳,在底層會走reallySetProperty
- 查看
objc_retain
id
objc_retain(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->retain();
}
當他是TaggedPointer
類型的時候,不走retain
直接返回旬昭。
- 查看
objc_release
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
同樣的篙螟,TaggedPointer
類型也不走release
。不需要進行內(nèi)存管理问拘,在常量區(qū)遍略,由系統(tǒng)釋放。小對象類型讀取比一般對象快了3倍骤坐,創(chuàng)建快了100倍绪杏。
在read_images中有一個處理TaggedPointer
的方法
- 點擊查看
initializeTaggedPointerObfuscator
static void
initializeTaggedPointerObfuscator(void)
{
if (sdkIsOlderThan(10_14, 12_0, 12_0, 5_0, 3_0) ||
// Set the obfuscator to zero for apps linked against older SDKs,
// in case they're relying on the tagged pointer representation.
DisableTaggedPointerObfuscation) {
objc_debug_taggedpointer_obfuscator = 0;
} else {
// 在iOS 10.14之后,objc_debug_taggedpointer_obfuscator與上~_OBJC_TAG_MASK纽绍,做了一次處理
arc4random_buf(&objc_debug_taggedpointer_obfuscator,
sizeof(objc_debug_taggedpointer_obfuscator));
objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
}
}
-
搜索
objc_debug_taggedpointer_obfuscator
蕾久,可以找到以下代碼
taggedpointer
在編碼和解碼的時候,指針ptr
要做一次與objc_debug_taggedpointer_obfuscator
的異或操作 -
驗證
將self.name
的原地址與objc_debug_taggedpointer_obfuscator
異或得到真正的地址拌夏,而且地址中的61
僧著,正是a
的ASCII
值
這不僅是一個簡單的地址,還包含了值障簿。
- 查看
_objc_isTaggedPointer
# define _OBJC_TAG_MASK (1UL<<63)
static inline bool
_objc_isTaggedPointer(const void * _Nullable ptr)
{
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
在64位中盹愚,取最高位,如果有值就是TaggedPointer
對象站故。
- 查看
objc_tag_index_t
這是TaggedPointer
的flag
類型皆怕,2
表示NSString
,3
表示NSNumber
,4
表示NSIndexPath
MRC & ARC
retain
從objc_retain
開始
id
objc_retain(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->retain();
}
- 查看
objc_object::retain()
inline id
objc_object::retain()
{
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
}
會走rootRetain
方法端逼。
- 查看
rootRetain
ALWAYS_INLINE id
objc_object::rootRetain()
{
return rootRetain(false, false);
}
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
//判斷是否為nonpointer isa朗兵,不是nonpointer_isa操作散列表rentain
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (rawISA()->isMetaClass()) return (id)this;
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
//判斷是否正在析構(gòu),沒有必要操作引用計數(shù)
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
uintptr_t carry;//引用計數(shù)+1
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
//carry 是一個標識位顶滩,代表你的引用計數(shù)位用完了余掖,這時候要借助散列表存儲引用計數(shù)
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
//引用計數(shù)滿了之后,把引用計數(shù)的一半存在extra_rc中
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
// 引用計數(shù)的另一半存到散列表中
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
release
- 查看
objc_release
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
TaggedPointer
對象直接返回
- 查看
release()
inline void
objc_object::release()
{
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
rootRelease();
return;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
}
調(diào)用rootRelease
- 查看
rootRelease
ALWAYS_INLINE bool
objc_object::rootRelease()
{
return rootRelease(true, false);
}
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
//非nonpointer_isa直接處理散列表
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (rawISA()->isMetaClass()) return false;
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
uintptr_t carry;
//引用計數(shù)減1 這里的carry礁鲁,是標志著isa里的 extra_rc不夠減盐欺,需要去散列表處理
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
goto retry;
}
// 從散列表中拿出滿值的一半
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
if (borrowed > 0) {
// 滿值的一半減1 賦值給isa的extra_rc
newisa.extra_rc = borrowed - 1;
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
if (!stored) {
// Inline update failed.
// Put the retains back in the side table.
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// Decrement successful after borrowing from side table.
// This decrement cannot be the deallocating decrement - the side
// table lock and has_sidetable_rc bit ensure that if everyone
// else tried to -release while we worked, the last one would block.
sidetable_unlock();
return false;
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
//dealloc函數(shù)
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
// does not actually return
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return true;
}
retainCount
查看retainCount
源碼
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;//引用計數(shù)加1
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();//加上散列表里的引用計數(shù)
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();//非nonpointer 直接返回散列表里的引用計數(shù)
}
當為nonpointer時,返回的引用計數(shù)額外加1仅醇。
非nonpointer時冗美,返回散列表里的引用計數(shù)。
dealloc
dealloc
在前面已經(jīng)分析過了傳送們
散列表
查看SideTable
源碼
struct SideTable {
spinlock_t slock;//操作散列表時要開解鎖
RefcountMap refcnts;//引用計數(shù)表
weak_table_t weak_table;//弱引用表
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
- 查看
SideTablesMap
真機里
StripeCount
為8析二,最多有8張表
NSTimer使用問題
1. NSTimer要加入runLoop才會執(zhí)行
上圖代碼粉洼,timeRun
方法并不會執(zhí)行,創(chuàng)建NSTimer
時使用timerWith
方法叶摄,需要將timer
加入到runLoop
属韧;或者使用scheduledTimer
創(chuàng)建NSTimer
//timerWithTimeInterval創(chuàng)建的需要加入到NSRunLoop
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timeRun) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
//scheduledTimerWithTimeInterval創(chuàng)建
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeRun) userInfo:nil repeats:YES];
2. weakSelf
接著在控制臺上打印
可以看到
self
和weakSelf
指針指向同一塊地址,而他們本身的地址不同蛤吓。
3. NSTimer的強引用
頁面退出后宵喂,controller
沒有走dealloc
方法,self.timer
沒有被釋放
處理方法:
- 在
didMoveToParentViewController
釋放timer
- (void)didMoveToParentViewController:(UIViewController *)parent {
[self.timer invalidate];
self.timer = nil;
}
- 使用
NSTimer
的block
形式
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
static int i = 0;
i++;
NSLog(@"%d",i);
}];
可是這里出現(xiàn)了新的問題会傲,雖然VC
釋放了锅棕,但是timer
還在打印
下面將探索這兩個問題的由來。
- 為什么
VC
釋放不掉淌山?timer
釋放不掉裸燎? - 使用
block
的形式,為什么VC
可以釋放了?
NSTimer
在foundation
庫中泼疑,未開源德绿。這里我們只能查看官方文檔,command + shift + 0
搜索timerWithTimeInterval
timer
會對target
保持一個強引用王浴,知道timer
釋放。
既然是這里的強引用引發(fā)的問題梅猿,那我們使用__weak
是不是就能解決問題呢氓辣?
//timerWithTimeInterval創(chuàng)建的需要加入到NSRunLoop
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer timerWithTimeInterval:1 target:weakSelf selector:@selector(timeRun) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
查看結(jié)果:
打印正常,還是沒有走
dealloc
方法袱蚓。__weak
不能解決
[NSRunLoop currentRunLoop]
強持有 -> timer
self -> block -> weakSelf
self -> timer -> weakSelf -> self
這兩個模型是不一樣的钞啸,block
捕捉的是指針地址,timer
捕捉的是內(nèi)存。
這是
block
的源碼体斩,拿到的是指針地址梭稚。如果傳入的是weakSelf
,這里拿到的就是weakSelf
的指針地址絮吵,與原本的self
已經(jīng)沒有關(guān)系了弧烤。而timer
是直接強持有<SecondViewController: 0x7fc048412da0>
這片內(nèi)存,故__weak
不起作用蹬敲。
既然原因我們知道了暇昂,那么如何解決這個問題呢?
解決思路:打破這一層強持有
思路一:VC
的dealloc
不能來伴嗡,那么我們手動銷毀timer
急波,在合適的地方把timer
銷毀了,timer
銷毀了瘪校,對VC
的強持有就沒有了澄暮,能夠調(diào)用dealloc
上述在didMoveToParentViewController
銷毀timer
就是這樣的例子。
思路二:中介者模式阱扬。不讓timer
強持有self
泣懊,給一個中介者
//中介者模式
self.target = [NSObject alloc];
class_addMethod([NSObject class], @selector(timeRun), (IMP)timeRun, "v@:");
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.target selector:@selector(timeRun) userInfo:nil repeats:YES];
思路三:虛基類Proxy
- (void)viewDidLoad {
[super viewDidLoad];
self.proxy = [LYProxy proxyWithTransformObject:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.proxy selector:@selector(timeRun) userInfo:nil repeats:YES];
}
@interface LYProxy : NSProxy
+ (instancetype)proxyWithTransformObject:(id)object;
@end
@interface LYProxy()
@property (nonatomic, weak) id object;
@end
@implementation LYProxy
+ (instancetype)proxyWithTransformObject:(id)object{
LYProxy *proxy = [LYProxy alloc];
proxy.object = object;
return proxy;
}
// 僅僅添加了weak類型的屬性還不夠,為了保證中間件能夠響應(yīng)外部self的事件价认,需要通過消息轉(zhuǎn)發(fā)機制嗅定,讓實際的響應(yīng)target還是外部self,這一步至關(guān)重要用踩,主要涉及到runtime的消息機制渠退。
// 轉(zhuǎn)移
// 強引用 -> 消息轉(zhuǎn)發(fā)
-(id)forwardingTargetForSelector:(SEL)aSelector {
return self.object;
}
- (void)dealloc{
NSLog(@"%s",__func__);
}
這樣就不會強引用VC
,在VC
釋放的時候脐彩,銷毀timer
碎乃,即可釋放proxy
。
4. @autoreleasepool自動釋放池
首先思考這幾個問題:
- 臨時變量什么時候釋放惠奸?
- 自動釋放池原理梅誓?
- 自動釋放池能否嵌套使用?
在libobjc
源碼中佛南,main
方法就有一個autoreleasepool
梗掰。
使用
clang
編譯成.cpp
文件可以看到多了一行__AtAutoreleasePool
代碼。
-
全局搜索
__AtAutoreleasePool
可以看到他是一個結(jié)構(gòu)體嗅回,里面有一個atautoreleasepoolobj
變量及穗,還有構(gòu)造方法和析構(gòu)方法。
這也就意味著绵载,autoreleasepool
創(chuàng)建的時候會調(diào)用objc_autoreleasePoolPush()
埂陆,析構(gòu)的時候會調(diào)用objc_autoreleasePoolPop
-
開啟匯編苛白,驗證
-
按住
control + step into
可以看到源碼在libobjc.A.dylib
庫中,接下來去libobjc.A.dylib
查看源碼就可以了焚虱。 -
在
libobjc
源碼中搜索Autorelease Pool
- 線程的自動釋放池购裙,是一堆指針。指針都指向?qū)⒁会尫诺膶ο缶樵裕蛘呤浅刈拥倪吔纭?/li>
- pop的時候躏率,所有對象都會釋放。
- 堆棧被分為一個雙向鏈接的頁面列表谍咆。
- 線程本地存儲禾锤,也可以保存自動釋放對象。
下面開始驗證環(huán)節(jié):
- 搜索
objc_autoreleasePoolPush
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
-
點擊進入
AutoreleasePoolPage
AutoreleasePoolPage
是一個類摹察,繼承于AutoreleasePoolPageData
-
查看
AutoreleasePoolPageData
其中有一個parent
和child
恩掷,這也就驗證了這是一個雙向鏈表。
magic:
用來校驗AutoreleasePoolPage的結(jié)構(gòu)是否完整
next:
指向最新添加的autoreleased
對象的下一個位置供嚎,初始化時指向begin()
thread:
指向當前線程
parent:
父節(jié)點黄娘,第一個節(jié)點的parent
值為nil
child:
子節(jié)點,最后一個節(jié)點的child
值為nil
depth:
深度克滴,從0開始逼争,往后遞增1
hiwat:
代表high water mark
最大入棧數(shù)量標記
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this));
}
當前地址 + 自身屬性的內(nèi)存大小
- 返回查看
push()
static inline void *push()
{
id *dest;
if (slowpath(DebugPoolAllocation)) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
autoreleaseNewPage
和autoreleaseFast
其實差不多,都是取到hotpage
劝赔,把POOL_BOUNDARY
哨兵加進去誓焦。
到這里,AutoreleasePoolPage
準備工作就做完了着帽,下面看一下如何把對象加入其中杂伟。
- 借助
_objc_autoreleasePoolPrint
打印釋放池情況
關(guān)閉ARC
,排除ARC
的影響
打印結(jié)果如下
上面的兩個是哨兵對象仍翰,由于我這里是嵌套了
@autoreleasepool
赫粥,所以有兩個哨兵對象。下面五個是NSObject
對象予借。
下面我們看一下page
的上限在哪越平?把循環(huán)次數(shù)改為1000
打印結(jié)果如下:
繼續(xù)往下翻,可以找到這樣的結(jié)構(gòu)灵迫。
這里重新生成了一張hot page
秦叛,地址后面從0
開始。
這里可以計算一下瀑粥,一張表可以存儲505
個對象挣跋,這一頁的大小是4096
字節(jié),也就是4kb
這里還需要注意一點利凑,在新的一頁中浆劲,并沒有新增哨兵對象。
- 查看
AutoreleasePoolPage
結(jié)構(gòu)體
size
在i386
中確實為4096
字節(jié)
接下來查看- autorelease
方法哀澈,
-
匯編查看
-
下符號斷點
objc_autorelease
-
運行
可以看出確實是調(diào)用了objc_autorelease
方法 搜索
objc_autorelease(
id
objc_autorelease(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->autorelease();
}
TaggedPointer
對象直接return
- 查看
autorelease
這里走的是rootAutorelease
方法
- 查看
rootAutorelease
走rootAutorelease2
方法
- 查看
rootAutorelease2
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
ASSERT(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
實際上走的是AutoreleasePoolPage
的autorelease()
方法
- 查看
autorelease(id obj)
static inline id autorelease(id obj)
{
ASSERT(obj);
ASSERT(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
這里就到了autoreleaseFast
方法牌借,與上面加入哨兵是同一個方法。
- 查看
autoreleaseFast
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
- 判斷
頁存在
并且沒滿
直接添加obj - 判斷
頁存在
并且滿了
創(chuàng)建新頁割按,添加obj - 頁不存時膨报,創(chuàng)建頁添加obj
- 查看
add(id obj)
id *add(id obj)
{
ASSERT(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
這里可以很明顯的看出next
指向了最新加入的obj
后面的位置,也就是下一個obj
地址
- 查看
autoreleaseFullPage
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
// The hot page is full.
// Step to the next non-full page, adding a new page if necessary.
// Then add the object to that page.
ASSERT(page == hotPage());
ASSERT(page->full() || DebugPoolAllocation);
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
- 不斷遍歷子節(jié)點适荣,查找一個沒有滿的
page
现柠,如果沒有則新建一個page
,上一個page
的child
指向new page
- 把
page
設(shè)置為hot
弛矛,加入obj
接下來查看pop
方法够吩,看page
中的數(shù)據(jù)如何出來。
- 搜索
objc_autoreleasePoolPop
NEVER_INLINE
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
- 點擊查看
pop
方法
static inline void
pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;//對空頁面的處理
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
page = hotPage();
if (!page) {
// Pool was never used. Clear the placeholder.
return setHotPage(nil);
}
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
page = coldPage();
token = page->begin();
} else {
page = pageForPointer(token);
}
stop = (id *)token;
if (*stop != POOL_BOUNDARY) {//不是邊界
if (stop == page->begin() && !page->parent) {//等于begin() 沒有父節(jié)點 不用處理
// Start of coldest page may correctly not be POOL_BOUNDARY:
// 1. top-level pool is popped, leaving the cold page in place
// 2. an object is autoreleased with no pool
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
return badPop(token);//
}//這是一個容錯處理
}
if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
return popPageDebug(token, page, stop);
}
return popPage<false>(token, page, stop);
}
真正要研究的是最下面的popPage
方法
- 查看
popPage
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
if (allowDebug && PrintPoolHiwat) printHiwat();
//遍歷page中的所有 obj丈氓,release obj
page->releaseUntil(stop);
// memory: delete empty children 銷毀所有的空頁面
if (allowDebug && DebugPoolAllocation && page->empty()) {
//page 為空銷毀自身周循,設(shè)置父節(jié)點為hot page
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (allowDebug && DebugMissingPools && page->empty() && !page->parent) {
//沒有父節(jié)點,銷毀當前page
page->kill();
setHotPage(nil);
} else if (page->child) {
//page 容量少于一半 child銷毀
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {//page的子節(jié)點 還有子節(jié)點 就銷毀
page->child->child->kill();
}
}
}
在這個方法中銷毀空page
- 查看
releaseUntil
void releaseUntil(id *stop)
{
//一直循環(huán)万俗,stop為哨兵對象的值 一直pop和release obj 直到哨兵對象為止
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
//如果page為空湾笛,指向他的parent
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;//obj 賦值為page中的最后一個元素
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));//清理page->next
page->protect();
if (obj != POOL_BOUNDARY) {
objc_release(obj);//obj release
}
}
setHotPage(this);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
ASSERT(page->empty());
}
#endif
}
在這個方法中pop
和release obj
對象