探究Autorelease本质

一、先创建一个Dog类,重写dealloc方法,看看Dog类的实例什么时候释放。工程改为MRC 工程:

探究Autorelease本质
文章图片
image.png 【探究Autorelease本质】
探究Autorelease本质
文章图片
image.png 发现实例并没有释放,在MRC 下需要添加 autorelease 或则 release
探究Autorelease本质
文章图片
image.png 二、c++ 重写 main.m
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m -o main-arm64.cpp

int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; Dog *g = ((Dog *(*)(id, SEL))(void *)objc_msgSend)((id)((Dog *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Dog"), sel_registerName("alloc")), sel_registerName("init")); } return 0; }

struct __AtAutoreleasePool { __AtAutoreleasePool() {//构造函数,在创建结构体时调用 atautoreleasepoolobj = objc_autoreleasePoolPush(); } ~__AtAutoreleasePool() {//析构函数,在销毁结构体时调用 objc_autoreleasePoolPop(atautoreleasepoolobj); } void * atautoreleasepoolobj; };

1.__AtAutoreleasePool() { atautoreleasepoolobj = objc_autoreleasePoolPush(); }:构造函数,在创建结构体时调用
2.~__AtAutoreleasePool() { objc_autoreleasePoolPop(atautoreleasepoolobj); }:析构函数,在销毁结构体时调用
3.所以上面代码可以改为:
伪代码
{ __AtAutoreleasePool __autoreleasepool; atautoreleasepoolobj = objc_autoreleasePoolPush(); Dog *g = [[[Dog alloc] init] autorelease]; objc_autoreleasePoolPop(atautoreleasepoolobj); }

__autoreleasepool 是一个局部变量,大括号结束就会调用objc_autoreleasePoolPop,将autorelease 对象释放。
三、看源码实现
void * objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push(); }void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt); }

class AutoreleasePoolPage { PAGE_MAX_SIZE; magic_t const magic; id *next; pthread_t const thread; AutoreleasePoolPage * const parent; AutoreleasePoolPage *child; uint32_t const depth; uint32_t hiwat; }

以上源码只保留重要部分。

探究Autorelease本质
文章图片
704C76E6-6322-4DD3-9751-FA296AB96C79.png 1.AutoreleasePoolPage 本质是这么一个结构,大小是4096字节。(PAGE_MAX_SIZE:4096,源码得到)。
2.前7个变量都是8字节,剩下的4040字节存储着autorelease对象地址
3.AutoreleasePoolPage * const parent; AutoreleasePoolPage *child; :由此可知当4040字节无法放下autorelease对象地址时,会扩展一个AutoreleasePoolPage,且该数据结构是,双向链表

探究Autorelease本质
文章图片
54B8E5AE-2A59-4604-8CC1-20FE67033480.png
4.next 指针指向当前 autorelease对象地址位置,每push 一个 对象,next 就会++。
id *add(id obj) { assert(!full()); unprotect(); id *ret = next; // faster than `return next-1` because of aliasing *next++ = obj; protect(); return ret; }

5.POOL_BOUNDARY 边界:# define POOL_BOUNDARY nil
比如:
@ autoreleasepool{ obj2 @ autoreleasepool{ obj1 } }

每次添加一个@ autoreleasepool,就相当于push一次POOL_BOUNDARY,出大括号,就pop到最近的POOL_BOUNDARY,也就是obj1 被pop 了。接着obj2 离开大括号,也就是pop到最近的POOL_BOUNDARY操作。
嵌套的AutoreleasePool:pop的时候总会释放到上次push的位置为止,多层的pool就是多个哨兵对象而已。
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 对象的时候会先pushPOOL_BOUNDARY 边界。
id * begin() { return (id *) ((uint8_t *)this+sizeof(*this)); }id * end() { return (id *) ((uint8_t *)this+SIZE); }

begin:7个成员变量之后的开始位置
end:结束位置
7.releaseUntil(begin()); :release 直到 begin
hotPage():当前AutoreleasePoolPage
coldPage:非当前AutoreleasePoolPage
四、runloop 、 autorelease、子线程
1.主线程默认为我们开启 Runloop,Runloop 会自动帮我们创建Autoreleasepool,并进行Push、Pop 等操作来进行内存管理。
2.子线程默认不开启runloop,当产生autorelease对象时候,会将对象添加到AutoreleasePoolPage中,也就是不手动创建Autoreleasepool也能正确释放对象,线程销毁时release对象
3.自定义的 NSOperation 和 NSThread 需要手动创建自动释放池。比如: 自定义的 NSOperation 类中的 main 方法里就必须添加自动释放池。否则出了作用域后,自动释放对象会因为没有自动释放池去处理它,而造成内存泄露。
但对于 blockOperation 和 invocationOperation 这种默认的Operation ,系统已经帮我们封装好了,不需要手动创建自动释放池。
4.AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程),没开一个线程,会有与之对应的AutoreleasePool
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; }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); } }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", pthread_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); }

Autoreleasepool 销毁时机也就是autorelease 对象销毁时机

    推荐阅读