AutoreleasePool实现原理


AutoreleasePool,根据名字可以知道这是一个自动释放池,起作用就是延缓自动释放池中对象的释放时机。

自动释放池是如何运作的

在iOS开发中,最经常见到的AutoreleasePool就是存在于main函数中的这个:

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

使用clang命令重写这部分OC代码:

xcrun -sdk iphones14.3 clang -rewrite-objc main.m

可以得到简化的代码:

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};


int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
    }
    return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}

可以看出,AutoreleasePool内部其实依靠结构体__AtAutoreleasePool来实现的:

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

而这个结构体中除了构造函数和析构函数之外,只有一个atautoreleasepoolobj指针变量.
在构造函数中,调用了objc_autoreleasePoolPush方法:

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

AutoreleasePoolPage的实现:

class AutoreleasePoolPage 
{
    // EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is 
    // pushed and it has never contained any objects. This saves memory 
    // when the top level (i.e. libdispatch) pushes and pops pools but 
    // never uses them.
#   define EMPTY_POOL_PLACEHOLDER ((id*)1)

#   define POOL_BOUNDARY nil
    static pthread_key_t const key = AUTORELEASE_POOL_KEY;
    static uint8_t const SCRIBBLE = 0xA3;  // 0xA3A3A3A3 after releasing
    static size_t const SIZE = 
#if PROTECT_AUTORELEASEPOOL
        PAGE_MAX_SIZE;  // must be multiple of vm page size
#else
        PAGE_MAX_SIZE;  // size and alignment, power of 2
#endif
    static size_t const COUNT = SIZE / sizeof(id);

// 用来校验 AutoreleasePoolPage 的结构是否完整
    magic_t const magic;
// 指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin();
    id *next;
// 指向当前线程
    pthread_t const thread;
    // 指向当前AutoreleasePage的上游节点,第一个AutoreleasePoolPage的parent = nil
    AutoreleasePoolPage * const parent;
        // 指向当前AutoreleasePage的下游节点,最后一个AutoreleasePoolPage的child = nil
    AutoreleasePoolPage *child;
    // 表示当前AutoreleasePoolPage所处的深度,从 0 开始,往后递增 1
    uint32_t const depth;
    // high water mark,相当于一个阀值,表示自动释放池中最大能存储对象的个数
    uint32_t hiwat;

...
...

AutoreleasePoolPage的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;
    }

而在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);
        }
    }

所以可以发现,调用push方法会有三种可能:

  • 当前的autoreleasepool对象已经存在hotPage(即不是第一次初始化调用push), 且当前的hotPage并未完全填充,则会在当前hotPage中add当前需要autorelease的对象obj;
    id *add(id obj)
    {
        assert(!full());
        // 设置指定内存区域可读可写
        unprotect();
        // 获取到next指针
        id *ret = next;  // faster than `return next-1` because of aliasing
        // 将obj存储在next指针指向的空间,同时next做自增运算
        *next++ = obj;
        // 设置内存区域只读
        protect();
        // 返回存储obj空间的指针
        return ret;
    }
  • 当前的autoreleasepool对象已经存在hotPage(即不是第一次初始化调用push), 且当前的hotPage完全填充,则会创建新的AuroreleasePage对象page,将page设置为hotPage,最后在当前hotPage中add当前需要autorelease的对象obj;
    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);
// 在链表中通过child指针查找尾部元素
        do {
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);
        } while (page->full());

        // 将新创建的AutoreleasePoolPage对象设置为当前的hotPage
        setHotPage(page);
        // 将当前
        return page->add(obj);
    }

  • 当前的autoreleasepool对象不存在hotPage(即第一次初始化调用push),会调用autoreleaseNoPage方法:
    static __attribute__((noinline))
    id *autoreleaseNoPage(id obj)
    {
        // "No page" 自动释放吃尚未被创建或者创建了一个占位的自动释放池,当其中并未包含任何对象,由此可以看出在第一次调用push方法时,系统并没有创建真正用于存储autorelease对象的自动释放池,而是创建了一个展位用的"池",表示第一次调用push方法的标志,只有当真实存储第一个autorelease对象时才会创建真正的自动释放。这一操作保证了只有在有对象需要存储时才会有自动释放池的内存开辟,避免了由于只是添加了自动释放池但并没有真正存储对象时造成的内存浪费

        assert(!hotPage());

        bool pushExtraBoundary = false;
        // 如果当前已经存在占位池,则需要在正式向栈中推入真实的对象之前添加POOL_BOUNDARY(作为pool的边界哨兵)
        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", 
                         pthread_self(), (void*)obj, object_getClassName(obj));
            objc_autoreleaseNoPool(obj);
            return nil;
        }
        // 在第一次调用push时,只是创建了一个展位池,并未创建真实的AutoreleasePage
        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

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

        //  传入的token是EMPTY_POOL_PLACEHOLDER
        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // Popping the top-level placeholder pool.
            if (hotPage()) {
                //当前自动释放池被使用过(自动释放池中存储过对象),则清空内容,但是保留开辟的空间已备重用
                pop(coldPage()->begin());
            } else {
                // 当前自动释放池从未被使用过(自动释放池没有存储过对象),则清除掉占位page
                setHotPage(nil);
            }
            return;
        }

        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 (PrintPoolHiwat) printHiwat();

        // 删除当前page中的(next,stop)位置处的对象
        page->releaseUntil(stop);

        // memory: delete empty children
        if (DebugPoolAllocation  &&  page->empty()) {
            // 特殊情况:在调试模式下删除当前界面的所有内容
            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) {
            // 如果当前界面保存的对象个数少于一般,则版留一个空子page
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

其中,pageForPointer用于获取参数p所在的page分页:

    static AutoreleasePoolPage *pageForPointer(const void *p) 
    {
        return pageForPointer((uintptr_t)p);
    }
        static AutoreleasePoolPage *pageForPointer(uintptr_t p) 
    {
        AutoreleasePoolPage *result;
        uintptr_t offset = p % SIZE;

        assert(offset >= sizeof(AutoreleasePoolPage));

        result = (AutoreleasePoolPage *)(p - offset);
        result->fastcheck();

        return result;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值