AutoreleasePool的實現

博客連接AutoreleasePool的實現編程

在MRC中,調用[obj autorelease]來延遲內存的釋放;在ARC下,對象調用autorelease方法,就會被自動添加到最近的自動釋放池,只有當自動釋放池被銷燬的時候,纔會執行release方法,進行釋放。真實結果究竟是什麼,等看完源碼後咱們就會知道了。緩存

AutoreleasePool

main.m中有一段代碼:bash

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
複製代碼

轉換成C++代碼:app

int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}
複製代碼

@autoreleasepool變成__AtAutoreleasePool __autoreleasepool__AtAutoreleasePool結構體定義以下:less

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};
複製代碼

它提供了兩個方法:objc_autoreleasePoolPush()objc_autoreleasePoolPop。這兩個方法的定義在NSObject.mm文件中,分別是:函數

void *
objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}

void
objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}
複製代碼

因此,autoreleasepool的自動釋放的核心就是AutoreleasePoolPage類oop

AutoreleasePoolPage

NSObject.mm文件中定義了AutoreleasePoolPage,這裏咱們只顯示這個類比較重要的屬性,以下:測試

class AutoreleasePoolPage {
    static size_t const SIZE = PAGE_MAX_SIZE;//SIZE是AutoreleasePoolPage的大小,4096個字節
    magic_t const magic; //autoreleasepool完整性校驗
    id *next;//AutoreleasePoolPage單個節點是一個鏈表,next指向棧頂的最新的autorelease對象的下一個位置
    pthread_t const thread;//當前所在的線程
    AutoreleasePoolPage * const parent;//指針
    AutoreleasePoolPage *child;//指針
    uint32_t const depth;//深度
    uint32_t hiwat;
}
複製代碼

經過源碼咱們能夠知道:優化

  • AutoreleasePool並無特定的內存結構,它是經過以AutoreleasePoolPage爲節點的雙向鏈表。
  • 每個AutoreleasePoolPage節點是一個堆棧結,且大小爲4096個字節。
  • 一個AutoreleasePoolPage節點對應着一個線程,屬於一一對應關係。

AutoreleasePool結構如圖所示:ui

AutoreleasePoolPage鏈表

接着咱們看一下AutoreleasePoolPage的構造函數以及一些操做方法:

//構造函數
    AutoreleasePoolPage(AutoreleasePoolPage *newParent) 
        : magic(), next(begin()), thread(pthread_self()),
          parent(newParent), child(nil), 
          depth(parent ? 1+parent->depth : 0), 
          hiwat(parent ? parent->hiwat : 0)
    { 
        if (parent) {
            parent->check();
            assert(!parent->child);
            parent->unprotect();
            parent->child = this;
            parent->protect();
        }
        protect();
    }
    
    //相關操做方法
    id * begin() {
        return (id *) ((uint8_t *)this+sizeof(*this));
    }

    id * end() {
        return (id *) ((uint8_t *)this+SIZE);
    }

    bool empty() {
        return next == begin();
    }

    bool full() { 
        return next == end();
    }

    bool lessThanHalfFull() {
        return (next - begin() < (end() - begin()) / 2);
    }

    id *add(id obj)
    {
        assert(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;
        protect();
        return ret;
    }
複製代碼
  • begin() 表示了一個AutoreleasePoolPage節點開始存autorelease對象的位置。
  • end() 一個AutoreleasePoolPage節點最大的位置
  • empty() 若是next指向beigin()說明爲空
  • full() 若是next指向end)說明滿了
  • id *add(id obj) 添加一個autorelease對象,next指向下一個存對象的地址。

因此一個空的AutoreleasePoolPage的結構以下:

AutoreleasePoolPage

AutoreleasePoolPage::push()

push代碼以下:

static inline void *push() 
    {
        id *dest;
        if (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;
    }
複製代碼

push執行的時候首先會進行判斷,若是是須要每一個pool都生成一個新page,即DebugPoolAllocationYES,則執行autoreleaseNewPage方法,不然執行autoreleaseFast方法。

autoreleaseNewPage

autoreleaseNewPage分爲兩種狀況:

  1. 當前存在page執行autoreleaseFullPage方法;
  2. 當前不存在pageautoreleaseNoPage方法。

autoreleaseFast

autoreleaseFast分爲三種狀況:

  1. 存在page且未滿,經過add()方法進行添加;
  2. 當前page已滿執行autoreleaseFullPage方法;
  3. 當前不存在page執行autoreleaseNoPage方法。

hotPage

前面講到的page其實就是hotPage,經過AutoreleasePoolPage *page = hotPage();獲取。

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;
    }
複製代碼

經過上面的代碼咱們知道當前頁是存在TLS(線程私有數據)裏面的。因此說第一次調用push的時候,沒有page天然連hotPage也沒有

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);
    }
複製代碼

autoreleaseFullPage會從傳入的page開始遍歷整個雙向鏈表,若是page滿了,就看它的child節點,直到查找到一個未滿的AutoreleasePoolPage。接着使用AutoreleasePoolPage構造函數傳入parent建立一個新的AutoreleasePoolPage的節點(此時跳出了while循環)。

在查找到一個可使用的AutoreleasePoolPage以後,會將該頁面標記成hotPage,而後調動add()方法添加對象。

autoreleaseNoPage

static __attribute__((noinline))
    id *autoreleaseNoPage(id obj)
    {
        //"no page"意味着沒有沒有池子被push或者說push了一個空的池子
        assert(!hotPage());

        bool pushExtraBoundary = false;
        if (haveEmptyPoolPlaceholder()) {//push了一個空的池子
            pushExtraBoundary = true;
        }
        else if (obj != POOL_BOUNDARY  &&  DebugMissingPools) {
            _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                         "autoreleased with no pool in place - "
                         "just leaking - break on "
                         "objc_autoreleaseNoPool() to debug", 
                         pthread_self(), (void*)obj, object_getClassName(obj));
            objc_autoreleaseNoPool(obj);
            return nil;
        }
        else if (obj == POOL_BOUNDARY  &&  !DebugPoolAllocation) {
            //沒有池子被push
            return setEmptyPoolPlaceholder();
        }

        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
        setHotPage(page);
        
        if (pushExtraBoundary) {
            //push了一個空的池子,添加哨兵對象
            page->add(POOL_BOUNDARY);
        }
    
        return page->add(obj);
    }
    
    //haveEmptyPoolPlaceholder的本質
        static inline bool haveEmptyPoolPlaceholder()
    {
        id *tls = (id *)tls_get_direct(key);
        return (tls == EMPTY_POOL_PLACEHOLDER);
    }
複製代碼

從上面的代碼咱們能夠知道,既然當前內存中不存在AutoreleasePoolPage,就要從頭開始構建這個自動釋放池的雙向鏈表,也就是說,新的AutoreleasePoolPage是沒有parent指針的。

初始化以後,將當前頁標記爲hotPage,而後會先向這個page中添加一個POOL_BOUNDARY的標記,來確保在pop調用的時候,不會出現異常。

最後,將obj添加到自動釋放池中。

autorelease方法

接着看一下當對象調用autorelase方法發生了什麼。

- (id)autorelease {
    return ((id)self)->rootAutorelease();
}

inline id 
objc_object::rootAutorelease()
{
    if (isTaggedPointer()) return (id)this;
    if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;

    return rootAutorelease2();
}

__attribute__((noinline,used))
id 
objc_object::rootAutorelease2()
{
    assert(!isTaggedPointer());
    return AutoreleasePoolPage::autorelease((id)this);
}

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;
}
複製代碼

從上面的源碼咱們看到,對象調用autorelase方法,最後會變成AutoreleasePoolPageautorelease函數。AutoreleasePoolPageautorelease的本質就是調用autoreleaseFast(obj)函數。只不過push操做插入的是一個POOL_BOUNDARY ,而autorelease操做插入的是一個具體的autoreleased對象即AutoreleasePoolPage入棧操做。

固然這麼說並不嚴謹,由於咱們須要考慮是不是Tagged Pointer和是否進行優化的狀況(prepareOptimizedReturn這個後面也會提到),若是不知足這兩個條件纔會進入緩存池。

因此push的流程是:

AutoreleasePoolPush流程

AutoreleasePoolPage::pop(ctxt)

static inline void pop(void *token) 
    {
        AutoreleasePoolPage *page;
        id *stop;

        //第一種狀況:autoreleasepool首次push的時候返回的,也就是最頂層的page執行pop會執行這一部分
        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // Popping the top-level placeholder pool.
            if (hotPage()) {
                // Pool was used. Pop its contents normally.
                // Pool pages remain allocated for re-use as usual.
                pop(coldPage()->begin());
            } else {
                // Pool was never used. Clear the placeholder.
                setHotPage(nil);
            }
            return;
        }

        page = pageForPointer(token);
        
        //https://stackoverflow.com/questions/24952549/does-nsthread-create-autoreleasepool-automatically-now
        //第二種狀況:在非ARC的狀況下,在新建立的線程中不使用autoreleasepool,直接調用autorelease方法時會出現這個狀況。此時沒有pool,直接進行autorelease。
        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 (PrintPoolHiwat) printHiwat();
        //第三種狀況:也就是咱們常常碰到的狀況
        page->releaseUntil(stop);

        // memory: delete empty children
        if (DebugPoolAllocation  &&  page->empty()) {
            // special case: delete everything during page-per-pool debugging
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (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

void releaseUntil(id *stop) {
    while (this->next != stop) {
        AutoreleasePoolPage *page = hotPage();

        while (page->empty()) {
            page = page->parent;
            setHotPage(page);
        }

        page->unprotect();
        id obj = *--page->next;
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
        page->protect();

        if (obj != POOL_BOUNDARY) {
            objc_release(obj);
        }
    }

    setHotPage(this);
}
複製代碼

從next指針開始,一個一個向前調用objc_release,直到碰到push時壓入的pool爲止。

因此autoreleasePool的運行過程應該是:

pool1 = push()
...
    pool2 = push()
    ...
        pool3 = push()
        ...
        pop(pool3)
    ...
    pop(pool2)
...
pop(pool1)
複製代碼

每次pop,實際上都會把最近一次push以後添加進去的對象所有release掉。

AutoreleasePool、Runloop、線程之間的關係

蘋果的文檔中提到:

Each NSThread object, including the application’s main thread, has an NSRunLoop object automatically created for it as needed.

The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event.

Each thread (including the main thread) maintains its own stack of NSAutoreleasePool objects.

咱們能夠知道:

  1. 每個線程,包括主線程,都會擁有一個專屬的runloop,而且會在有須要的時候自動建立。

  2. 主線程在runloop開始以前會自動建立一個autoreleasePool,並在結束時pop。那其餘的線程呢?這裏我作了一個實驗,代碼以下:

  3. 每個線程都會維護本身的autoreleasePool堆棧,也就是說每個autoreleasePool對應一個線程。

進入AutoreleasePool的時機

那什麼樣的對象會進入autoreleasePool呢?

測試

測試1.1代碼

NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
複製代碼

結果

2019-01-22 15:50:45.129263+0800 AutoreleasePool[31529:22121744] 1
objc[31529]: ##############
objc[31529]: AUTORELEASE POOLS for thread 0x1176345c0
objc[31529]: 2 releases pending.
objc[31529]: [0x7f96e0802000]  ................  PAGE  (hot) (cold)
objc[31529]: [0x7f96e0802038]    0x600003ac8f00  __NSArrayI
objc[31529]: [0x7f96e0802040]    0x600000ceef80  __NSSetI
objc[31529]: ##############
複製代碼

測試1.2代碼

@autoreleasepool{
    NSMutableArray *arr = [NSMutableArray new];
    NSLog(@"%lu", _objc_rootRetainCount(arr));
    _objc_autoreleasePoolPrint();
}
複製代碼

結果

2019-01-22 15:53:28.818873+0800 AutoreleasePool[31568:22134125] 1
objc[31568]: ##############
objc[31568]: AUTORELEASE POOLS for thread 0x10d20e5c0
objc[31568]: 3 releases pending.
objc[31568]: [0x7fcf66002000]  ................  PAGE  (hot) (cold)
objc[31568]: [0x7fcf66002038]    0x600000129200  __NSArrayI
objc[31568]: [0x7fcf66002040]    0x60000370b020  __NSSetI
objc[31568]: [0x7fcf66002048]  ################ POOL 0x7fcf66002048
objc[31568]: ##############
複製代碼

測試1.3代碼

{
    NSMutableArray *arr = [NSMutableArray new];
    NSLog(@"%lu", _objc_rootRetainCount(arr));
    _objc_autoreleasePoolPrint();
}

@autoreleasepool{
    NSMutableArray *arr = [NSMutableArray new];
    NSLog(@"%lu", _objc_rootRetainCount(arr));
     _objc_autoreleasePoolPrint();
}
複製代碼

結果

2019-01-22 15:55:21.271452+0800 AutoreleasePool[31596:22141965] 1
objc[31596]: ##############
objc[31596]: AUTORELEASE POOLS for thread 0x1166f15c0
objc[31596]: 2 releases pending.
objc[31596]: [0x7fdcaf002000]  ................  PAGE  (hot) (cold)
objc[31596]: [0x7fdcaf002038]    0x600003e6a500  __NSArrayI
objc[31596]: [0x7fdcaf002040]    0x600000849db0  __NSSetI
objc[31596]: ##############
2019-01-22 15:55:21.272353+0800 AutoreleasePool[31596:22141965] 1
objc[31596]: ##############
objc[31596]: AUTORELEASE POOLS for thread 0x1166f15c0
objc[31596]: 3 releases pending.
objc[31596]: [0x7fdcaf002000]  ................  PAGE  (hot) (cold)
objc[31596]: [0x7fdcaf002038]    0x600003e6a500  __NSArrayI
objc[31596]: [0x7fdcaf002040]    0x600000849db0  __NSSetI
objc[31596]: [0x7fdcaf002048]  ################ POOL 0x7fdcaf002048
objc[31596]: ##############
複製代碼

測試2.1代碼

NSMutableArray *arr = [NSMutableArray array];
 NSLog(@"%lu", _objc_rootRetainCount(arr));
 _objc_autoreleasePoolPrint();
複製代碼

結果

2019-01-22 15:57:02.360860+0800 AutoreleasePool[31615:22149043] 2
objc[31615]: ##############
objc[31615]: AUTORELEASE POOLS for thread 0x1111eb5c0
objc[31615]: 3 releases pending.
objc[31615]: [0x7fbf00002000]  ................  PAGE  (hot) (cold)
objc[31615]: [0x7fbf00002038]    0x600003f15c00  __NSArrayI
objc[31615]: [0x7fbf00002040]    0x6000009705f0  __NSSetI
objc[31615]: [0x7fbf00002048]    0x600002404f30  __NSArrayM
objc[31615]: ##############
複製代碼

測試2.2代碼

@autoreleasepool {
    NSMutableArray *arr = [NSMutableArray array];
    NSLog(@"%lu", _objc_rootRetainCount(arr));
    _objc_autoreleasePoolPrint();
}
複製代碼

結果

2019-01-22 15:58:29.932693+0800 AutoreleasePool[31634:22153810] 2
objc[31634]: ##############
objc[31634]: AUTORELEASE POOLS for thread 0x115aac5c0
objc[31634]: 4 releases pending.
objc[31634]: [0x7f867c002000]  ................  PAGE  (hot) (cold)
objc[31634]: [0x7f867c002038]    0x600000b00080  __NSArrayI
objc[31634]: [0x7f867c002040]    0x600003d64190  __NSSetI
objc[31634]: [0x7f867c002048]  ################ POOL 0x7f867c002048
objc[31634]: [0x7f867c002050]    0x60000100ff30  __NSArrayM
objc[31634]: ##############
複製代碼

測試2.3代碼

{
    NSMutableArray *arr = [NSMutableArray array];
    NSLog(@"%lu", _objc_rootRetainCount(arr));
    _objc_autoreleasePoolPrint();
}

@autoreleasepool {
    NSMutableArray *arr = [NSMutableArray array];
    NSLog(@"%lu", _objc_rootRetainCount(arr));
     _objc_autoreleasePoolPrint();
}
複製代碼

結果

2019-01-22 16:01:11.925690+0800 AutoreleasePool[31670:22164284] 2
objc[31670]: ##############
objc[31670]: AUTORELEASE POOLS for thread 0x11bffa5c0
objc[31670]: 3 releases pending.
objc[31670]: [0x7ff965802000]  ................  PAGE  (hot) (cold)
objc[31670]: [0x7ff965802038]    0x600001c1eb00  __NSArrayI
objc[31670]: [0x7ff965802040]    0x600002a47200  __NSSetI
objc[31670]: [0x7ff965802048]    0x600000712490  __NSArrayM
objc[31670]: ##############
2019-01-22 16:01:11.926577+0800 AutoreleasePool[31670:22164284] 1
objc[31670]: ##############
objc[31670]: AUTORELEASE POOLS for thread 0x11bffa5c0
objc[31670]: 4 releases pending.
objc[31670]: [0x7ff965802000]  ................  PAGE  (hot) (cold)
objc[31670]: [0x7ff965802038]    0x600001c1eb00  __NSArrayI
objc[31670]: [0x7ff965802040]    0x600002a47200  __NSSetI
objc[31670]: [0x7ff965802048]    0x600000712490  __NSArrayM
objc[31670]: [0x7ff965802050]  ################ POOL 0x7ff965802050
objc[31670]: ##############****
複製代碼

從上面的代碼咱們能夠知道,使用newalloc這樣的方法建立的對象實例是不會進入autoreleasePool的,可是使用簡便方法建立的對象如[NSMutableArray array]是會進入自動緩存池的

可是在測試2.3上,咱們能夠看到,只有一個array進入了自動緩存池,另一個沒有進入。看一下它的方法調用棧:

1548145578479

在《Objective-C高級編程》第66-67頁提到了最優化程序運行。經過objc_retainAutoreleasedReturnValueobjc_retainAutoreleaseReturnValue函數的協做,能夠不將對象註冊到autoreleasePool中而直接傳遞,這一過程達到最優化。

objc_retainAutoreleasedReturnValue

objc_retainAutoreleasedReturnValue的實現以下:

// Accept a value returned through a +0 autoreleasing convention for use at +1.
id
objc_retainAutoreleasedReturnValue(id obj)
{
    if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;

    return objc_retain(obj);
}

// Try to accept an optimized return.
// Returns the disposition of the returned object (+0 or +1).
// An un-optimized return is +0.
static ALWAYS_INLINE ReturnDisposition 
acceptOptimizedReturn()
{
    ReturnDisposition disposition = getReturnDisposition();
    setReturnDisposition(ReturnAtPlus0);  // reset to the unoptimized state
    return disposition;
}

static ALWAYS_INLINE ReturnDisposition 
getReturnDisposition()
{
    return (ReturnDisposition)(uintptr_t)tls_get_direct(RETURN_DISPOSITION_KEY);
}
複製代碼

經過上面的代碼咱們能夠知道objc_retainAutoreleasedReturnValue會嘗試接收一個被優化的結果,如何是ReturnAtPlus1YES,返回對象自己,不然執行objc_retain(obj

這個被優化的結果是在線程私有數據TLS中的,咱們能夠理解爲一個優化位。當優化位返回YES的時候,直接返回對象自己,不然執行retain。

objc_retainAutoreleaseReturnValue

objc_retainAutoreleaseReturnValue的實現以下:

// Prepare a value at +0 for return through a +0 autoreleasing convention.
id 
objc_retainAutoreleaseReturnValue(id obj)
{
    if (prepareOptimizedReturn(ReturnAtPlus0)) return obj;

    // not objc_autoreleaseReturnValue(objc_retain(obj)) 
    // because we do not need another optimization attempt
    return objc_retainAutoreleaseAndReturn(obj);
}

// Try to prepare for optimized return with the given disposition (+0 or +1).
// Returns true if the optimized path is successful.
// Otherwise the return value must be retained and/or autoreleased as usual.
static ALWAYS_INLINE bool 
prepareOptimizedReturn(ReturnDisposition disposition)
{
    assert(getReturnDisposition() == ReturnAtPlus0);

    if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {
        if (disposition) setReturnDisposition(disposition);
        return true;
    }

    return false;
}

static ALWAYS_INLINE bool 
callerAcceptsOptimizedReturn(const void * const ra0)
{
    const uint8_t *ra1 = (const uint8_t *)ra0;
    const unaligned_uint16_t *ra2;
    const unaligned_uint32_t *ra4 = (const unaligned_uint32_t *)ra1;
    const void **sym;

#define PREFER_GOTPCREL 0
#if PREFER_GOTPCREL
    // 48 89 c7    movq  %rax,%rdi
    // ff 15       callq *symbol@GOTPCREL(%rip)
    if (*ra4 != 0xffc78948) {
        return false;
    }
    if (ra1[4] != 0x15) {
        return false;
    }
    ra1 += 3;
#else
    // 48 89 c7    movq  %rax,%rdi
    // e8          callq symbol
    if (*ra4 != 0xe8c78948) {
        return false;
    }
    ra1 += (long)*(const unaligned_int32_t *)(ra1 + 4) + 8l;
    ra2 = (const unaligned_uint16_t *)ra1;
    // ff 25       jmpq *symbol@DYLDMAGIC(%rip)
    if (*ra2 != 0x25ff) {
        return false;
    }
#endif
    ra1 += 6l + (long)*(const unaligned_int32_t *)(ra1 + 2);
    sym = (const void **)ra1;
    if (*sym != objc_retainAutoreleasedReturnValue  &&  
        *sym != objc_unsafeClaimAutoreleasedReturnValue) 
    {
        return false;
    }

    return true;
}

// Same as objc_retainAutorelease but suitable for tail-calling 
// if you do not want to push a frame before this point.
__attribute__((noinline))
static id 
objc_retainAutoreleaseAndReturn(id obj)
{
    return objc_retainAutorelease(obj);
}

id
objc_retainAutorelease(id obj)
{
    return objc_autorelease(objc_retain(obj));
}
複製代碼

這裏主要涉及到callerAcceptsOptimizedReturn,這個函數意思不是很理解,可是裏面涉及到了objc_retainAutoreleasedReturnValue,猜想多是程序檢測在返回值以後是否緊接着調用了objc_retainAutoreleasedReturnValue,若是是,就知道了外部是ARC環境走優化路線,反之就走沒被優化的邏輯。

因此我的認爲,使用newalloc這樣的方法建立的對象實例是不會進入autoreleasePool的,可是使用簡便方法建立的對象,程序會進行優化後,再決定是否進入自動緩存池

另外關於main函數中的@autoreleasepool的做用是什麼?簡單的說就是讓那些進入自動緩存池的對象有個地方被釋放。

總結

  1. 在APP中,整個主線程是運行在一個自動釋放池中的。

  2. main函數中的自動釋放池的做用:這個池塊給出了一個pop點來顯式的告訴咱們這裏有一個釋放點,若是你的main在初始化的過程當中有別的內容能夠放在這裏。

  3. 使用@autoreleasepool標記,調用push()方法。

  4. 沒有hotpage,調用autoreleaseNoPage(),設置EMPTY_POOL_PLACEHOLDER

  5. 由於設置了EMPTY_POOL_PLACEHOLDER,因此會設置本頁爲hotpage,添加邊界標記POOL_BOUNDARY,最後添加obj。

  6. 繼續有對象調用autorelease,此時已經有了page,調用page->add(obj)

  7. 若是page滿了,調用autoreleaseFullPage()建立新page,重複第6點。

  8. 到達autoreleasePool邊界,調用pop方法,一般狀況下會釋放掉POOL_BOUNDARY以後的全部對象

相關文章
相關標籤/搜索