AutoreleasePool的实现

原文链接AutoreleasePool的实现

在MRC中,调用[obj autorelease]来延迟内存的释放;在ARC下,对象调用autorelease方法,就会被自动添加到最近的自动释放池,只有当自动释放池被销毁的时候,才会执行release方法,进行释放。真实结果到底是什么,等看完源码后我们就会知道了。

AutoreleasePool

main.m中有一段代码:

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

转换成C++代码:

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结构体定义如下:

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类

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结构如图所示:
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。那其他的线程呢?这里我做了一个实验,代码如下:

  1. 每一个线程都会维护自己的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 don't 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 don't 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之后的所有对象

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容