iOS - AutoreleasePool - 底層篇

[toc]

參考

AutoreleasePool 底層

私有函數(shù)打印

可以通過以下私有函數(shù)來查看自動(dòng)釋放池的情況:

// extern 聲明系統(tǒng)內(nèi)部的函數(shù)
extern void _objc_autoreleasePoolPrint(void);
示例1
extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool { //  r1 = push()
        Person *p1 = [[[Person alloc] init] autorelease];
        Person *p2 = [[[Person alloc] init] autorelease];
        @autoreleasepool { // r2 = push()
            Person *p3 = [[[Person alloc] init] autorelease];
            @autoreleasepool { // r3 = push()
                Person *p4 = [[[Person alloc] init] autorelease]; 
                _objc_autoreleasePoolPrint(); // 調(diào)用該私有函數(shù)
            } // pop(r3)
            
        } // pop(r2)
    } // pop(r1)
    return 0;
}

控制臺(tái)輸出如下:
objc[1405]: ##############
objc[1405]: AUTORELEASE POOLS for thread 0x1000d2dc0
objc[1405]: 7 releases pending.
objc[1405]: [0x10300d000]  ................  PAGE  (hot) (cold)
objc[1405]: [0x10300d038]  ################  POOL 0x10300d038 // Boundary
objc[1405]: [0x10300d040]       0x1007adcd0  Person
objc[1405]: [0x10300d048]       0x1007acd90  Person
objc[1405]: [0x10300d050]  ################  POOL 0x10300d050
objc[1405]: [0x10300d058]       0x1007ab730  Person
objc[1405]: [0x10300d060]  ################  POOL 0x10300d060
objc[1405]: [0x10300d068]       0x1007acc50  Person
objc[1405]: ##############
示例2

一頁不夠的情況

int main(int argc, const char * argv[]) {
    @autoreleasepool { //  r1 = push()
        Person *p1 = [[[Person alloc] init] autorelease];
        Person *p2 = [[[Person alloc] init] autorelease];
        @autoreleasepool { // r2 = push()
            for (int i = 0; i < 600; i++) { // 這里 autorelease 了 600 個(gè)對(duì)象
                Person *p3 = [[[Person alloc] init] autorelease];
            }
            @autoreleasepool { // r3 = push()
                Person *p4 = [[[Person alloc] init] autorelease];
                _objc_autoreleasePoolPrint();
            } // pop(r3)
        } // pop(r2)
    } // pop(r1)
    return 0;
}

控制臺(tái)輸出如下:
objc[1461]: ##############
objc[1461]: AUTORELEASE POOLS for thread 0x1000d2dc0
objc[1461]: 606 releases pending.
objc[1461]: [0x10300d000]  ................  PAGE (full)  (cold)
objc[1461]: [0x10300d038]  ################  POOL 0x10300d038
objc[1461]: [0x10300d040]       0x1005addd0  Person
objc[1461]: [0x10300d048]       0x1005ace90  Person
objc[1461]: [0x10300d050]  ################  POOL 0x10300d050
objc[1461]: [0x10300d058]       0x1005ab830  Person
.
.
.
objc[1461]: [0x10300dff8]       0x1005b5d50  Person
objc[1461]: [0x10300a000]  ................  PAGE  (hot) // 當(dāng)前page; 上一頁容量不夠, 換頁了
objc[1461]: [0x10300a038]       0x1005b5d60  Person
.
.
.
objc[1461]: [0x10300a348]       0x1005b6380  Person
objc[1461]: [0x10300a350]  ################  POOL 0x10300a350
objc[1461]: [0x10300a358]       0x1005b6390  Person
objc[1461]: ##############
Program ended with exit code: 0

源碼分析:

  • objc4源碼 NSObject.mm
  • clang重寫 @autoreleasepool{}

objc4源碼

https://opensource.apple.com/tarballs/objc4/

clang重寫

OC代碼:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello, World!");
    }
    return 0;
}
轉(zhuǎn)成C++
int main(int argc, const char * argv[]) {
    {
        __AtAutoreleasePool __autoreleasepool;
        // 為簡潔, 這里精簡了string
        NSLog((NSString *)&__NSConstantStringImpl__var_xxxxxx_mi_0); 
    }
    return 0;
}

可以看到: @autoreleasepool {} 在編譯時(shí)被轉(zhuǎn)換為一個(gè) __AtAutoreleasePool 類型的局部變量 __autoreleasepool ; ★

__AtAutoreleasePool

結(jié)構(gòu)

__AtAutoreleasePool 是個(gè)結(jié)構(gòu)體, 定義如下: (定義在.cpp上部)

struct __AtAutoreleasePool {
    // __AtAutoreleasePool的構(gòu)造函數(shù)
    __AtAutoreleasePool() { atautoreleasepoolobj = objc_autoreleasePoolPush(); } 
    // __AtAutoreleasePool的析構(gòu)函數(shù)
    ~__AtAutoreleasePool() { objc_autoreleasePoolPop(atautoreleasepoolobj); } 
    // POOL_BOUNDARY 地址, push的返回值; pop的入?yún)?    void * atautoreleasepoolobj;
};
實(shí)現(xiàn)原理

可以看到, __AtAutoreleasePool 這個(gè)結(jié)構(gòu)體會(huì)在:

  • 初始化時(shí), 調(diào)用 objc_autoreleasePoolPush() 方法;
  • 在析構(gòu)時(shí), 調(diào)用 objc_autoreleasePoolPop() 方法;

根據(jù)C++ 構(gòu)造函數(shù)析構(gòu)函數(shù) 的特點(diǎn):

  • 自動(dòng)局部變量的 構(gòu)造函數(shù), 是在程序執(zhí)行到聲明這個(gè)對(duì)象的位置時(shí)調(diào)用的;
  • 自動(dòng)局部變量的 析構(gòu)函數(shù), 是在程序執(zhí)行到離開這個(gè)對(duì)象的作用域時(shí)調(diào)用;

所以:

  • 聲明 __autoreleasepool 變量時(shí), 構(gòu)造函數(shù) __AtAutoreleasePool() 被調(diào)用;

    即執(zhí)行: atautoreleasepoolobj = objc_autoreleasePoolPush();

  • 出當(dāng)前作用域時(shí), 析構(gòu)函數(shù) ~__AtAutoreleasePool() 被調(diào)用;

    即執(zhí)行: objc_autoreleasePoolPop(atautoreleasepoolobj);

即, 在 __autoreleasepool 初始化和析構(gòu)時(shí), 分別調(diào)用 objc_autoreleasePoolPush()objc_autoreleasePoolPop() ; ★

所以可以認(rèn)為,

當(dāng)我們使用 @autoreleasepool{} 時(shí), 編譯器實(shí)際上將其轉(zhuǎn)化為以下代碼:

int main(int argc, const char * argv[]) {
    {
        // 即將進(jìn)入runloop, 調(diào)用push(), 創(chuàng)建pool, 
        void *atautoreleasepoolobj = objc_autoreleasePoolPush(); 
        
        NSLog((NSString *)&__NSConstantStringImpl__var_xxxxxx_mi_0);
        
        // 即將退出當(dāng)前runloop時(shí), 調(diào)用pop(), 釋放pool
        objc_autoreleasePoolPop(atautoreleasepoolobj); 
    }
    return 0;
}

可見, @autoreleasepool{} 只是幫助我們封裝了 objc_autoreleasePoolPush()objc_autoreleasePoolPop() 這兩行代碼, 讓代碼看起來更簡潔足绅。★

至此, 我們可以分析出, 單個(gè)自動(dòng)釋放池的運(yùn)行過程可簡單概括為:

objc_autoreleasePoolPush() 
[object autorelease] ;
objc_autoreleasePoolPop(void *)

objc_autoreleasePoolPush()objc_autoreleasePoolPop() 這兩個(gè)方法實(shí)際是對(duì) AutoreleasePoolPage 對(duì)應(yīng)的靜態(tài)方法 push()pop() 的封裝纵潦。

// NSObject.mm
void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}

void objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}

push()pop() 見下節(jié) AutoreleasePoolPage 分析


AutoreleasePoolPage ★

簡介

AutoreleasePool 沒有單獨(dú)的結(jié)構(gòu), 而是由若干個(gè) AutoreleasePoolPage雙向鏈表的形式組合而成何什。 ★

每個(gè) AutoreleasePoolPage 的容量是 4096 字節(jié) (0x1000, 4k, 也就是虛擬內(nèi)存每個(gè)扇區(qū)的大小, 4k對(duì)齊由此而來), 除了用來存放它內(nèi)部的成員變量(56B)生宛,剩下的空間(4040B)用來存放autorelease對(duì)象的地址;

圖示
image
結(jié)構(gòu)
class AutoreleasePoolPage
// AutoreleasePoolPage 是一個(gè) C++ 的類, 定義在 NSObject.mm 中
class AutoreleasePoolPage : private AutoreleasePoolPageData {
    // 空池占位
#   define EMPTY_POOL_PLACEHOLDER ((id*)1)  
    
    // 邊界對(duì)象, 老版本源碼中變量名是 POOL_SENTINEL(哨兵對(duì)象), 用來區(qū)別每個(gè) AutoreleasePoolPage 邊界
#   define POOL_BOUNDARY nil 
    
    // 
    static pthread_key_t const key = AUTORELEASE_POOL_KEY; 
    // 0xA3A3A3A3 after releasing
    static uint8_t const SCRIBBLE = 0xA3;  
    // PAGE_MAX_SIZE = 4096, 每個(gè) AutoreleasePoolPage 的大小
    static size_t const SIZE = PAGE_MAX_SIZE;  
    // 一個(gè)page里對(duì)象數(shù)
    static size_t const COUNT = SIZE / sizeof(id); 

    /// 下面這7個(gè)成員, 每個(gè)占8字節(jié), 共56字節(jié) ★
    
    magic_t const magic; // 用于對(duì)當(dāng)前 AutoreleasePoolPage 進(jìn)行完整性校驗(yàn);
    
    // 指向最新添加的 autoreleased 對(duì)象的下一個(gè)位置, 初始化時(shí)指向 begin(); 
    // 指向下一個(gè)能夠存放 autoreleased 對(duì)象地址的位置, 相當(dāng)于一個(gè)游標(biāo)
    id *next; 
    pthread_t const thread; // 指向當(dāng)前線程;  AutoreleasePool 是按線程一一對(duì)應(yīng)的 ★
    
    AutoreleasePoolPage * const parent; // 父結(jié)點(diǎn), 指向前一個(gè)page, 第一個(gè)結(jié)點(diǎn)的 parent 值為 nil;
    AutoreleasePoolPage *child; // 子結(jié)點(diǎn), 指向下一個(gè)page, 最后一個(gè)結(jié)點(diǎn)的 child 值為 nil;
    
    uint32_t const depth; // 鏈表的深度, 節(jié)點(diǎn)個(gè)數(shù), 從 0 開始, 往后遞增 1;
    uint32_t hiwat; // 代表 high water mark; 數(shù)據(jù)容納的上限
    
    // ... 
};
struct AutoreleasePoolPageData
// class AutoreleasePoolPage : private AutoreleasePoolPageData
struct AutoreleasePoolPageData {
    magic_t const magic;
    __unsafe_unretained id *next;
    pthread_t const thread;
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;

    AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
        : magic(), next(_next), thread(_thread),
          parent(_parent), child(nil),
          depth(_depth), hiwat(_hiwat)
    {
    }
};
雙向鏈表

一個(gè) AutoreleasePoolPage 的空間被占滿時(shí), 會(huì)新建一個(gè) AutoreleasePoolPage 對(duì)象, 連接鏈表, 后來的 autorelease 對(duì)象在新的 page 加入。★

AutoreleasePoolPage 本質(zhì)是結(jié)構(gòu)體, 結(jié)構(gòu)體中包含 parent 和 child 指針, parent 指向前一個(gè) page, child 指向下一個(gè) page, 從而構(gòu)成雙向鏈表孵户。

AutoreleasePoolPage 的組織是個(gè)棧, 結(jié)構(gòu)成員 id *next 作為游標(biāo), 指向棧頂下一個(gè)為空的內(nèi)存地址兽狭『豆桑★

但他并不是指內(nèi)存中的棧區(qū);

AutoreleasePoolPage 是個(gè)對(duì)象, 內(nèi)存在堆區(qū);

begin()
// 起始地址
id * begin() {
    // 起始地址 + 自身大小
    return (id *) ((uint8_t *)this+sizeof(*this));
}
end()
// 結(jié)束地址
id * end() {
    // 起始地址 + 4096
    return (id *) ((uint8_t *)this+SIZE);
}

// 4096
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
        PAGE_MAX_SIZE;  // must be multiple of vm page size
#else
        PAGE_MIN_SIZE;  // size and alignment, power of 2
#endif
hotPage()
// 返回當(dāng)前正在使用的page
static inline AutoreleasePoolPage *hotPage() {
    AutoreleasePoolPage *result = (AutoreleasePoolPage *)tls_get_direct(key);
    if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
    if (result) result->fastcheck();
    return result;
}
push() ★
// 將 POOL_BOUNDARY 壓到 page 的內(nèi)存中, 占8字節(jié); 并返回其所在的內(nèi)存地址
// 存在嵌套的 pool 時(shí), 每來到一個(gè)`{`, 就會(huì)壓入1個(gè) POOL_BOUNDARY 到 page 中, 每個(gè) pool 對(duì)應(yīng)一個(gè) POOL_BOUNDARY  ★
static inline void *push() {
    id *dest;
    // 一開始沒有page, 創(chuàng)建新的 
    if (slowpath(DebugPoolAllocation)) {
        // Each autorelease pool starts on a new pool page.
        dest = autoreleaseNewPage(POOL_BOUNDARY); 
    } 
    // 本來就有page
    else {
        dest = autoreleaseFast(POOL_BOUNDARY);
    }
    ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
    return dest;
}
autoreleaseNewPage()
// 創(chuàng)建新的 page; 入?yún)?obj 為 POOL_BOUNDARY
static __attribute__((noinline)) id *autoreleaseNewPage(id obj) {
    AutoreleasePoolPage *page = hotPage();
    if (page) return autoreleaseFullPage(obj, page);
    else return autoreleaseNoPage(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); // 如果滿了 & 無子page, 傳入當(dāng)前page, 創(chuàng)建新page
    } while (page->full());

    setHotPage(page);
    // 將 POOL_BOUNDARY 壓入 page
    return page->add(obj);
}
AutoreleasePoolPage()
// 構(gòu)造函數(shù)
AutoreleasePoolPage(AutoreleasePoolPage *newParent) :
    AutoreleasePoolPageData(begin(),
                            objc_thread_self(),
                            newParent,
                            newParent ? 1+newParent->depth : 0,
                            newParent ? newParent->hiwat : 0)
{ 
    if (parent) {
        parent->check();
        ASSERT(!parent->child);
        parent->unprotect();
        parent->child = this;
        parent->protect();
    }
    protect();
}
autoreleaseFast()
// 本來就有page, 入?yún)?obj 為 POOL_BOUNDARY 或者 autoreleased 對(duì)象
static inline id *autoreleaseFast(id obj) {
    AutoreleasePoolPage *page = hotPage();
    
    if (page && !page->full()) { // 有熱頁, 沒滿
        // 添加到page
        return page->add(obj); 
    } else if (page) { // 有熱頁, 滿了
        return autoreleaseFullPage(obj, page);
    } else { // 沒有熱頁, 創(chuàng)建新頁
        return autoreleaseNoPage(obj);
    }
}
autoreleaseNoPage()
// 創(chuàng)建新頁; 入?yún)?POOL_BOUNDARY
static __attribute__((noinline)) id *autoreleaseNoPage(id obj) {
    // "No page" could mean no pool has been pushed
    // or an empty placeholder pool has been pushed and has no contents yet
    ASSERT(!hotPage());

    bool pushExtraBoundary = false;
    if (haveEmptyPoolPlaceholder()) {
        // We are pushing a second pool over the empty placeholder pool
        // or pushing the first object into the empty placeholder pool.
        // Before doing that, push a pool boundary on behalf of the pool 
        // that is currently represented by the empty placeholder.
        pushExtraBoundary = true;
    }
    else if (obj != POOL_BOUNDARY  &&  DebugMissingPools) {
        // We are pushing an object with no pool in place, 
        // and no-pool debugging was requested by environment.
        _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                     "autoreleased with no pool in place - "
                     "just leaking - break on "
                     "objc_autoreleaseNoPool() to debug", 
                     objc_thread_self(), (void*)obj, object_getClassName(obj));
        objc_autoreleaseNoPool(obj);
        return nil;
    }
    else if (obj == POOL_BOUNDARY  &&  !DebugPoolAllocation) {
        // We are pushing a pool with no pool in place,
        // and alloc-per-pool debugging was not requested.
        // Install and return the empty pool placeholder.
        return setEmptyPoolPlaceholder();
    }

    // We are pushing an object or a non-placeholder'd pool.

    // Install the first page.
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);

    // Push a boundary on behalf of the previously-placeholder'd pool.
    if (pushExtraBoundary) {
        page->add(POOL_BOUNDARY);
    }

    // Push the requested object or pool.
    return page->add(obj);
}
pop() ★
// 傳入之前 push() 所返回的 POOL_BOUNDARY 地址
// 會(huì)從最后一個(gè)壓入 page 的對(duì)象開始(棧頂), 依次調(diào)用其 release 方法, 直到這個(gè) POOL_BOUNDARY
// 存在嵌套pool時(shí), 每離開一個(gè) `}` 就會(huì)pop() 一次, 然后清理掉對(duì)應(yīng)的 `{` 之后的autoreleased對(duì)象, 以及該 POOL_BOUNDARY
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) {
            // 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()
static void popPage(void *token, AutoreleasePoolPage *page, id *stop) {
    if (allowDebug && PrintPoolHiwat) printHiwat();

    page->releaseUntil(stop);

    // memory: delete empty children
    if (allowDebug && DebugPoolAllocation  &&  page->empty()) {
        // special case: delete everything during page-per-pool debugging
        AutoreleasePoolPage *parent = page->parent;
        page->kill();
        setHotPage(parent);
    } else if (allowDebug && DebugMissingPools  &&  page->empty()  &&  !page->parent) {
        // special case: delete everything for pop(top)
        // when debugging missing autorelease pools
        page->kill();
        setHotPage(nil);
    } else if (page->child) {
        // hysteresis: keep one empty child if page is more than half full
        if (page->lessThanHalfFull()) {
            page->child->kill();
        }
        else if (page->child->child) {
            page->child->child->kill();
        }
    }
}
releaseUntil()
// 迭代釋放對(duì)象, 直到stop
void releaseUntil(id *stop)  {
    // Not recursive: we don't want to blow out the stack 
    // if a thread accumulates a stupendous amount of garbage

    while (this->next != stop) {
        // Restart from hotPage() every time, in case -release 
        // autoreleased more objects
        AutoreleasePoolPage *page = hotPage();
        // 處理跨頁問題 ★
        // fixme I think this `while` can be `if`, but I can't prove it
        while (page->empty()) {
            page = page->parent;
            setHotPage(page);
        }

        page->unprotect();
        id obj = *--page->next;
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
        page->protect();
        // 只要不是 POOL_BOUNDARY 就 release
        if (obj != POOL_BOUNDARY) {
            objc_release(obj);
        }
    }

    setHotPage(this);

#if DEBUG
    // we expect any children to be completely empty
    for (AutoreleasePoolPage *page = child; page; page = page->child) {
        ASSERT(page->empty());
    }
#endif
}

autorelease ★
autorelease
+ (id)autorelease {
    return (id)self;
}

// Replaced by ObjectAlloc
- (id)autorelease {
    return _objc_rootAutorelease(self);
}
_objc_rootAutorelease()
NEVER_INLINE id _objc_rootAutorelease(id obj) {
    ASSERT(obj);
    return obj->rootAutorelease();
}
rootAutorelease()
// Base autorelease implementation, ignoring overrides.
inline id  objc_object::rootAutorelease() {
    if (isTaggedPointer()) return (id)this;
    if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;

    return rootAutorelease2();
}
rootAutorelease2()
__attribute__((noinline,used)) id objc_object::rootAutorelease2() {
    ASSERT(!isTaggedPointer());
    // 傳入調(diào)用 autorelease 的對(duì)象
    return AutoreleasePoolPage::autorelease((id)this); 
}
autorelease() ☆
// AutoreleasePoolPage 的方法; 入?yún)?autoreleased 對(duì)象
static inline id autorelease(id obj) {
    ASSERT(obj);
    ASSERT(!obj->isTaggedPointer());
    // 調(diào)用 autoreleaseFast, 將 obj 壓入page
    id *dest __unused = autoreleaseFast(obj);
    ASSERT(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
    return obj;
}
POOL_BOUNDARY

push()時(shí):

autoreleasePool 初始化, 調(diào)用 objc_autoreleasePoolPush() ;

每次push都會(huì)創(chuàng)建一個(gè)新的 pool, 在 AutoreleasePoolPage 中的 next 指針指向的地址插入一個(gè) 邊界對(duì)象, 然后返回這個(gè)邊界對(duì)象的地址, 用于pop()時(shí)傳入 ;

將 next 指針重新指向棧頂下一個(gè)為空的內(nèi)存地址; (此時(shí)應(yīng)為邊界對(duì)象上面的地址) ;

pop()時(shí):

autoreleasePool 析構(gòu), 調(diào)用 objc_autoreleasePoolPop() ;

會(huì)銷毀當(dāng)前的 pool, 會(huì)向pool中晚于傳入的邊界對(duì)象插入的所有對(duì)象都發(fā)送一次 release 消息, 直到傳入的邊界對(duì)象; 從最后插入到pool的對(duì)象一直向前清理, 可以向前跨越若干個(gè)page, 直到邊界對(duì)象所在的page;

將 next 指針重新指向棧頂下一個(gè)為空的內(nèi)存地址; (此時(shí)應(yīng)為被釋放的邊界對(duì)象之前所處的地址)

autorelease()時(shí):

在 AutoreleasePoolPage 中的 next指針指向的地址插入 需要加入 autoreleasepool 的對(duì)象;

將next指針重新指向棧頂下一個(gè)為空的內(nèi)存地址; (此時(shí)應(yīng)為剛剛?cè)霔?duì)象上面的地址)

總結(jié)

自動(dòng)釋放池的主要底層數(shù)據(jù)結(jié)構(gòu)是:__AtAutoreleasePool、AutoreleasePoolPage

調(diào)用了autorelease的對(duì)象最終都是通過AutoreleasePoolPage對(duì)象來管理的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末箕慧,一起剝皮案震驚了整個(gè)濱河市服球,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌颠焦,老刑警劉巖斩熊,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伐庭,居然都是意外死亡粉渠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門圾另,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霸株,“玉大人,你說我怎么就攤上這事集乔∪ゼ” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵扰路,是天一觀的道長尤溜。 經(jīng)常有香客問我,道長幼衰,這世上最難降的妖魔是什么靴跛? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮渡嚣,結(jié)果婚禮上梢睛,老公的妹妹穿的比我還像新娘肥印。我一直安慰自己,他們只是感情好绝葡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布深碱。 她就那樣靜靜地躺著,像睡著了一般藏畅。 火紅的嫁衣襯著肌膚如雪敷硅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天愉阎,我揣著相機(jī)與錄音绞蹦,去河邊找鬼。 笑死榜旦,一個(gè)胖子當(dāng)著我的面吹牛幽七,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播溅呢,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼澡屡,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了咐旧?” 一聲冷哼從身側(cè)響起驶鹉,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铣墨,沒想到半個(gè)月后室埋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踏兜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年词顾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碱妆。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肉盹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疹尾,到底是詐尸還是另有隱情上忍,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布纳本,位于F島的核電站窍蓝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏繁成。R本人自食惡果不足惜吓笙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望巾腕。 院中可真熱鬧面睛,春花似錦絮蒿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至幌墓,卻和暖如春但壮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背常侣。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工蜡饵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胳施。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓验残,卻偏偏與公主長得像,于是被迫代替她去往敵國和親巾乳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348