阅读 232

iOS底层原理探索 ----- 内存管理(三)

资源准备

自动释放池

概述

AutoReleasePool:自动释放池

OC中的一种内存自动回收机制,它可以将加入AutoreleasePool中的变量release的时机延迟。

简单来说,当创建一个对象,在正常情况下,变量会在超出其作用域时立即release。如果将其加入到自动释放池中,这个对象并不会立即释放,而会等到runloop休眠/超出autoreleasepool作用域之后进行释放。

image.png

  • 从程序启动到加载完成,主线程对应的Runloop会处于休眠状态,等待用户交互来唤醒Runloop

  • 用户每次交互都会启动一次Runloop,用于处理用户的所有点击、触摸等事件;

  • Runloop在监听到交互事件后,就会创建自动释放池,并将所有延迟释放的对象添加到自动释放池中;

  • 在一次完整的Runloop结束之前,会向自动释放池中所有对象发送release消息,然后销毁自动释放池。

结构

使用cpp文件探索

创建一个Mac工程,在main.m中,自动生成以下代码:

#import <Foundation/Foundation.h> 

int main(int argc, const char * argv[]) { 
    @autoreleasepool { 
        NSLog(@"Hello, World!");
    } 
    
    return 0; 
}
复制代码

转化生成cpp文件:

clang -rewrite-objc main.m -o main.cpp
复制代码

打开cpp文件,来到main函数:

int main(int argc, const char * argv[]) { 
    /* @autoreleasepool */ {
        __AtAutoreleasePool __autoreleasepool; 
        
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_jl_d06jlfkj2ws74_5g45kms07m0000gn_T_main_da0d58_mi_0); 
    }
    return 0; 
}
复制代码
  • autoreleasepool被注释掉了,但作用域还在;

  • 作用域中生成对__AtAutoreleasePool类型声明的代码。

找到__AtAutoreleasePool定义:

struct __AtAutoreleasePool { 
    __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} 
    ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} 
    void * atautoreleasepoolobj; 
};
复制代码
  • __AtAutoreleasePool是一个结构体,包含构造函数和析构函数;

  • 结构体声明,触发构造函数,调用objc_autoreleasePoolPush函数;

  • 当结构体出作用域空间,触发析构函数,调用objc_autoreleasePoolPop函数。

使用汇编代码探索

打开项目,在main函数中autoreleasepool处设置断点,查看汇编代码:

image.png

  • 调用objc_autoreleasePoolPush函数;

  • 调用objc_autoreleasePoolPop函数。

进入objc_autoreleasePoolPush函数:

image.png

  • 源码来自于libobjc框架。

源码探索

打开objc4-818.2源码,找到objc_autoreleasePoolPush函数:

void 
* objc_autoreleasePoolPush(void) { 
    return AutoreleasePoolPage::push(); 
}
复制代码
  • 调用AutoreleasePoolPage命名空间下的push函数。

AutoreleasePoolPage

找到AutoreleasePoolPage的定义,首先看到这样一段注释:

/*********************************************************************** Autorelease pool implementation 

A thread's autorelease pool is a stack of pointers. 
线程的自动释放池是一个指针堆栈 

Each pointer is either an object to release, or POOL_BOUNDARY which is an autorelease pool boundary.
每个指针要么是一个要释放的对象,要么是POOL_BOUNDARY自动释放池边界 

A pool token is a pointer to the POOL_BOUNDARY for that pool. When the pool is popped, every object hotter than the sentinel is released.
池令牌是指向该池的POOL_BOUNDARY的指针。当池被弹出,每个比哨兵热的对象都被释放 

The stack is divided into a doubly-linked list of pages. Pages are added and deleted as necessary. 
堆栈被分成一个双链接的页面列表。根据需要添加和删除页面 

Thread-local storage points to the hot page, where newly autoreleased objects are stored. 
线程本地存储指向热页,其中存储新自动释放的对象 **********************************************************************/
复制代码

通过注释我们可以了解到以下几点:

  • 自动释放池和线程有关系;

  • 自动释放池是一个存储指针的栈结构;

  • 指针要么是一个要释放的对象,要么是POOL_BOUNDARY自动释放池边界,俗称:哨兵对象:

    • 哨兵对象的作用:当自动释放池将对象进行pop操作时,需要知道边界在哪里,否则会破坏别人的内存空间。而哨兵对象,就是作为边界的标识而存在。
  • 自动释放池的栈空间被分成一个双链接结构的页面列表,可添加和删除页面:

    • 双向链表的特别,一个页中同时存在父节点和子节点。可向前找到父页面,也可向后找到子页面。
  • 线程本地存储指向热页,其中存储新自动释放的对象:

    • 栈原则,先进后出,可以理解为最后一个页面就是热页。里面的对象最后被push,最先被pop

AutoreleasePoolPage继承于AutoreleasePoolPageData

class AutoreleasePoolPage : private AutoreleasePoolPageData { 
    friend struct thread_data_t; 

public: 
    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

private: 
    static pthread_key_t const key = AUTORELEASE_POOL_KEY; 
    static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing 
    static size_t const COUNT = SIZE / sizeof(id);
    static size_t const MAX_FAULTS = 2; 
    
    ... 
}
复制代码

AutoreleasePoolPageData

找到AutoreleasePoolPageData的定义:

class AutoreleasePoolPage; 
struct AutoreleasePoolPageData { 
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS 
    struct AutoreleasePoolEntry {
        uintptr_t ptr: 48; 
        uintptr_t count: 16;
        
        static const uintptr_t maxCount = 65535; // 2^16 - 1 
    };
    
    static_assert((AutoreleasePoolEntry) {.ptr = MACH_VM_MAX_ADDRESS }.ptr == MACH_VM_MAX_ADDRESS, "MACH_VM_MAX_ADDRESS doesn't fit into AutoreleasePoolEntry::ptr!"); 
#endif 
    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) { }
};
复制代码

结构体中,包含以下成员变量:

  • magic:用来校验AutoreleasePoolPage的结构是否完整;

  • next:指向最新添加的autoreleased对象的下一个位置,初始化时执行begin()

  • thread:指向当前线程;

  • parent:指向父节点,第一个节点的parent值为nil

  • child:指向子节点,最后一个节点的child值为nil

  • depth:代表深度,从0开始,往后递增1

  • hiwat:代表high water mark最大入栈数量标记。

打印结构

搭建测试项目,关闭ARC模式:

image.png

打开main.m文件,写入以下代码:

extern void _objc_autoreleasePoolPrint(void); 

int main(int argc, const char * argv[]) {
    @autoreleasepool { 
        NSObject *objc = [[[NSObject alloc] init] autorelease]; 
        
        _objc_autoreleasePoolPrint(); 
    }
    
    return 0; 
}
复制代码
  • 导入_objc_autoreleasePoolPrint函数,用于打印自动释放池的结构;

  • 创建NSObject实例对象,加入自动释放池;

  • 调用_objc_autoreleasePoolPrint函数,打印结构。

输出以下内容:

############## 
AUTORELEASE POOLS for thread 0x1000ebe00 
2 releases pending. 
[0x10700b000] ................ PAGE (hot) (cold) 
[0x10700b038] ################ POOL 0x10700b038 
[0x10700b040] 0x100705f60 NSObject 
##############
复制代码
  • 打印出当前自动释放池所属线程

  • 2个需要释放的对象

  • 当前的Page信息,占56字节。因为只有一页,即是冷页面,也是热页面

  • 哨兵对象POOL

  • NSObject对象

_objc_autoreleasePoolPrint

官方用于对自动释放池内容调试打印的函数:

void 
_objc_autoreleasePoolPrint(void) { 
    AutoreleasePoolPage::printAll();
}
复制代码

进入printAll函数

    static void printAll() {
        _objc_inform("##############");
        _objc_inform("AUTORELEASE POOLS for thread %p", objc_thread_self());

        AutoreleasePoolPage *page;
        ptrdiff_t objects = 0;
        for (page = coldPage(); page; page = page->child) {
            objects += page->next - page->begin();
        }

        _objc_inform("%llu releases pending.", (unsigned long long)objects);

        if (haveEmptyPoolPlaceholder()) {
            _objc_inform("[%p] ................ PAGE (placeholder)", EMPTY_POOL_PLACEHOLDER);_objc_inform("[%p] ################ POOL (placeholder)", EMPTY_POOL_PLACEHOLDER);
        } else {
            for (page = coldPage(); 
            page; page = page->child) {
                page->print();
            }
        }
        _objc_inform("##############");
    }
    
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
    __attribute__((noinline, cold)) unsigned sumOfExtraReleases() {
        unsigned sumOfExtraReleases = 0; 
        for (id *p = begin(); p < next; p++) { 
            if (*p != POOL_BOUNDARY) {
                sumOfExtraReleases += ((AutoreleasePoolEntry *)p)->count; 
            } 
        } 
        
        return sumOfExtraReleases; 
    } 
#endif

    __attribute__((noinline, cold)) static void printHiwat() {
        // Check and propagate high water mark 
        // Ignore high water marks under 256 to suppress noise. 
        AutoreleasePoolPage *p = hotPage(); 
        uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
        if (mark > p->hiwat + 256) { 
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
            unsigned sumOfExtraReleases = 0; 
#endif 
            for( ; p; p = p->parent) { 
                p->unprotect(); 
                p->hiwat = mark; 
                p->protect(); 
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
            sumOfExtraReleases += p->sumOfExtraReleases(); 
#endif 
        } 
        
        _objc_inform("POOL HIGHWATER: new high water mark of %u " 
                     "pending releases for thread %p:",
                     mark, objc_thread_self()); 
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
        if (sumOfExtraReleases > 0) {
            _objc_inform("POOL HIGHWATER: extra sequential autoreleases of objects: %u", sumOfExtraReleases);
        }
#endif
        void *stack[128]; 
        int count = backtrace(stack, sizeof(stack)/sizeof(stack[0])); 
        char **sym = backtrace_symbols(stack, count); 
        for (int i = 0; i < count; i++) {
            _objc_inform("POOL HIGHWATER: %s", sym[i]);
        }
        
        free(sym); 
      } 
   }
#undef POOL_BOUNDARY
};
复制代码
  • 按照自动释放池的结构,通过双向链表遍历page,依次读取page中的内容并进行打印。

对象压栈

进入objc_autoreleasePoolPush函数:

void * 
objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push(); 
}
复制代码

进入AutoreleasePoolPage命名空间下的push函数:

static inline void *push() { 
    id *dest;
    if (slowpath(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; 
}
复制代码
  • DebugPoolAllocation:当自动释放池按顺序弹出时停止,并允许堆调试器跟踪自动释放池;

  • 不存在,调用autoreleaseNewPage函数,从一个新的池页开始创建;

  • 否则,调用autoreleaseFast函数,将哨兵对象压栈。

autoreleaseFast

进入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);
    } 
}
复制代码
  • 如果存在page,并且没有存满,调用add函数;

  • 如果存在page,但存储已满,调用autoreleaseFullPage函数;

  • 否则,不存在page,调用autoreleaseNoPage函数。

autoreleaseNoPage

进入autoreleaseNoPage函数:

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; 
    
    ... 
    
    // 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);
}
复制代码
  • 调用AutoreleasePoolPage构造函数,创建新页;

  • 设置为热页面;

  • pushExtraBoundaryYES,哨兵对象压栈;

  • 对象压栈。

进入AutoreleasePoolPage构造函数:

AutoreleasePoolPage(AutoreleasePoolPage *newParent) :
    AutoreleasePoolPageData(begin(), 
                            objc_thread_self(), 
                            newParent, newParent ? 
                            1+newParent->depth : 0, newParent ?
                            newParent->hiwat : 0) {
    if (objc::PageCountWarning != -1) { 
        checkTooMuchAutorelease(); 
    } 
    
    if (parent) {
        parent->check(); 
        ASSERT(!parent->child);
        parent->unprotect(); 
        parent->child = this; 
        parent->protect(); 
    }
    
    protect(); 
}
复制代码
  • 通过父类AutoreleasePoolPageData进行初始化;

  • begin:获取对象压栈的起始位置;

  • objc_thread_self:通过tls获取当前线程;

  • 链接双向链表。

进入begin函数:

id * begin() { 
     return (id *) ((uint8_t *)this+sizeof(*this));
 }
复制代码
  • sizeof(*this):大小取决于自身结构体中的成员变量;

  • 返回对象可压栈的真正开始地址,在成员变量以下。

进入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; 
    
    ... 
};
复制代码
  • magic16字节

  • nextthreadparentchild:各占8字节

  • depthhiwat:各占4字节

  • 共占56字节

进入magic_t的定义:

struct magic_t { 
    static const uint32_t M0 = 0xA1A1A1A1; 
# define M1 "AUTORELEASE!" 
    static const size_t M1_len = 12; 
    uint32_t m[4]; 
    
    ... 
# undef M1 
};
复制代码
  • 结构体的创建在堆区申请内存,而静态成员存储在静态区,不占结构体大小;

  • 16字节来自于uint32_t数组。

进入objc_thread_self函数:

static inline pthread_t objc_thread_self() {
    return (pthread_t)tls_get_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF);
}
复制代码
  • 通过tls获取当前线程。

autoreleaseFullPage

进入autoreleaseFullPage函数:

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);
}
复制代码
  • 遍历链表,找到最后一个空白的子页面;

  • 对其进行创建新页;

  • 设置为热页面;

  • 添加对象。

形成以下数据结构:

image.png

add

进入add函数:

id *add(id obj) { 
    ASSERT(!full()); 
    unprotect(); 
    id *ret;
    
    ... 
    
    ret = next; 
    
    // faster than `return next-1` because of aliasing 
    *next++ = obj; 

#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
    // Make sure obj fits in the bits available for it 
    ASSERT(((AutoreleasePoolEntry *)ret)->ptr == (uintptr_t)obj); 
#endif 
 done: 
    protect(); 
    return ret; 
}
复制代码
  • 使用*next++进行内存平移;

  • 将对象压栈。

autoreleaseNewPage

进入autoreleaseNewPage函数:

id 
*autoreleaseNewPage(id obj) { 
    AutoreleasePoolPage *page = hotPage(); 
    if (page) 
        return autoreleaseFullPage(obj, page); 
    else 
        return autoreleaseNoPage(obj);
}
复制代码
  • 获取热页面;

  • 存在,调用autoreleaseFullPage函数;

  • 否则,不存在page,调用autoreleaseNoPage函数。

autorelease

进入objc_autorelease函数:

id 
objc_autorelease(id obj) {
    if (obj->isTaggedPointerOrNil()) return obj; 
    return obj->autorelease(); 
}
复制代码
  • 当前对象为TaggedPointernil,直接返回;

  • 否则,调用对象的autorelease函数。

进入autorelease函数:

inline id 
objc_object::autorelease() { 
    ASSERT(!isTaggedPointer()); 
    if (fastpath(!ISA()->hasCustomRR())) {
        return rootAutorelease();
    }
    
    return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(autorelease)); 
}
复制代码
  • 如果是自定义类,调用rootAutorelease函数。

进入rootAutorelease --> rootAutorelease2 --> AutoreleasePoolPage::autorelease函数:

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

id 
objc_object::rootAutorelease2() { 
    ASSERT(!isTaggedPointer()); 
    return AutoreleasePoolPage::autorelease((id)this);
} 

static inline id autorelease(id obj) { 
    ASSERT(!obj->isTaggedPointerOrNil()); 
    id *dest __unused = autoreleaseFast(obj); 
    
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS 
    ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || (id)((AutoreleasePoolEntry *)dest)->ptr == obj);
#else 
    ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj); 
#endif 
    return obj; 
}
复制代码
  • 调用autoreleaseFast函数,对象压栈。

池页容量

自动释放池采用分页的方式存储对象,因为对象在频繁压栈和出栈的过程中,产生异常,值会影响当前页面,不会影响到整个自动释放池。

并且自动释放池分页管理,每页之前的地址可以不连续,它们可以使用双向链表找到父页面和子页面。如果所有对象都使用一页存储,为了保证地址的连续性,每次扩容会相对繁琐和耗时:

int main(int argc, const char * argv[]) { 
    @autoreleasepool {
        for (int i = 0; i < 505; i++) {
            NSObject *objc = [[[NSObject alloc] init] autorelease];
        }
        _objc_autoreleasePoolPrint(); 
    } 
    return 0;
}
复制代码

打印结果:

objc[1804]: ############## 
objc[1804]: AUTORELEASE POOLS for thread 0x1000ebe00 
objc[1804]: 506 releases pending. 
objc[1804]: [0x10200c000] ................ PAGE (full) (cold) 
objc[1804]: [0x10200c038] ################ POOL 0x10200c038 
objc[1804]: [0x10200c040] 0x100638420 NSObject 
objc[1804]: [0x10200c048] 0x100637a40 NSObject 
objc[1804]: [0x10200c050] 0x100636970 NSObject 

... 

objc[1804]: [0x100809000] ................ PAGE (hot) 
objc[1804]: [0x100809038] 0x10063a0b0 NSObject 
objc[1804]: ##############
复制代码
  • 505NSObject对象循环加入自动释放池,当存储504个对象时,池页已满。第505个对象创建新池页存储;

  • 一页的容量:504 * 8 = 4032,加上56字节成员变量和8字节哨兵对象,共计4096字节;

  • 每一页都存在56字节的成员变量;

  • 一个自动释放池,只会压栈一个哨兵对象。

在源码中查看:

class AutoreleasePoolPage : private AutoreleasePoolPageData { 
    friend struct thread_data_t;
    
public: 
    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 

... 

}
复制代码

来到PAGE_MIN_SIZE的定义:

#define PAGE_MIN_SHIFT     12 
#define PAGE_MIN_SIZE      (1 << PAGE_MIN_SHIFT)
复制代码
  • 1左移12位,相当于2 ^ 12 = 4096

对象出栈

进入objc_autoreleasePoolPop函数:

void 
objc_autoreleasePoolPop(void *ctxt) { 
    AutoreleasePoolPage::pop(ctxt);
}
复制代码

进入pop函数:

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. 
            //不存在热页面,将标记设置为nil
            return setHotPage(nil); 
        } 
        
        // Pool was used. Pop its contents normally. 
        // Pool pages remain allocated for re-use as usual. 
        //存在热页面,通过双向链表循环向上找到最冷页面 
        page = coldPage();
        
        //将token设置为起始位置
        token = page->begin(); 
    } else { 
    
        //获取token所在的页 
        page = pageForPointer(token); 
    } 
    
    //赋值给stop 
    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 
            //最冷页面的起始可能不是POOL_BOUNDARY: 
            //1. 弹出顶级池,保留冷页面
            //2. 对象在没有池的情况下被自动释放 
        } 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

进入popPage函数:

static void 
popPage(void *token, AutoreleasePoolPage *page, id *stop) { 
    if (allowDebug && PrintPoolHiwat) printHiwat(); 
    
    //当前页中对象出栈,到stop位置停止 
    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 
        //特殊情况:删除所有的pop 
        
        //销毁当前页面 
        page->kill(); 
        
        //将热页面标记设置为nil 
        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

进入releaseUntil函数:

void releaseUntil(id *stop) { 
    // Not recursive: we don't want to blow out the stack 
    // if a thread accumulates a stupendous amount of garbage 
    
    //向下遍历,到stop停止 
    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();
        
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS 
        AutoreleasePoolEntry* entry = (AutoreleasePoolEntry*) --page->next; 

        // create an obj with the zeroed out top byte and release that 
        id obj = (id)entry->ptr; 
        int count = (int)entry->count;     // grab these before memset
#else 
        //内存平移,获取对象 
        id obj = *--page->next; 
    
#endif 
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next)); 
        page->protect(); 
        
        //当前对象不是哨兵对象
        if (obj != POOL_BOUNDARY) { 
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS 
            // release count+1 times since it is count of the additional
            // autoreleases beyond the first one
            for (int i = 0; i < count + 1; i++) { 
                objc_release(obj); 
            }
    #else 
            //将其释放 
            objc_release(obj);
#endif 
        } 
    } 
    
    //将当前页面标记为热页面 
    setHotPage(this); 
    
#if DEBUG 
    // we expect any children to be completely empty 
    for (AutoreleasePoolPage *page = child; page; page = page->child) { 
        ASSERT(page->empty()); 
    } 
#endif 
}
复制代码

kill

进入kill函数:

void kill() { 
    // Not recursive: we don't want to blow out the stack 
    // if a thread accumulates a stupendous amount of garbage 
    AutoreleasePoolPage *page = this; 
    
    //循环找到最后一个子页面
    while (page->child) page = page->child; 
    
    AutoreleasePoolPage *deathptr; 
    do { 
        deathptr = page; 
        
        //找到父页面 
        page = page->parent; 
        
        if (page) { 
            //将子页面设置为nil 
            page->unprotect();
            page->child = nil; 
            page->protect(); 
        } 
        
        //销毁子页面 
        delete deathptr; 
        
        //遍历销毁到this为止 
    } while (deathptr != this);
}
复制代码

嵌套使用

int main(int argc, const char * argv[]) {
    @autoreleasepool { 
        NSObject *objc = [[[NSObject alloc] init] autorelease]; 
        
        @autoreleasepool { 
            NSObject *objc = [[[NSObject alloc] init] autorelease]; 
        } 
        
        _objc_autoreleasePoolPrint(); 
    } 
    return 0; 
}
复制代码

打印结果:

objc[2511]: ############## 
objc[2511]: AUTORELEASE POOLS for thread 0x1000ebe00 
objc[2511]: 4 releases pending. 
objc[2511]: [0x10680d000] ................ PAGE (hot) (cold) 
objc[2511]: [0x10680d038] ################ POOL 0x10680d038 
objc[2511]: [0x10680d040] 0x101370c40 NSObject 
objc[2511]: [0x10680d048] ################ POOL 0x10680d048 
objc[2511]: [0x10680d050] 0x101365fb0 NSObject 
objc[2511]: ##############
复制代码
  • 自动释放池可以嵌套使用,存储结构和常规使用没有区别;

  • 子释放池存在自己的哨兵对象。

ARC模式

将项目设置为ARC模式:

image.png

创建LGPerson类:

#import <Foundation/Foundation.h> 

@interface LGPerson : NSObject

+ (LGPerson *)person; 

@end 


@implementation LGPerson

+ (LGPerson *)person {
    LGPerson *p = [[LGPerson alloc] init];
    return p; 
} 

@end
复制代码

main函数中,写入以下代码:

extern void _objc_autoreleasePoolPrint(void); 

int main(int argc, const char * argv[]) {
    @autoreleasepool { 
        LGPerson *p1 = [[LGPerson alloc] init]; 
        LGPerson *p2 = [LGPerson person];
        _objc_autoreleasePoolPrint(); 
    } 
    
    return 0; 
}
复制代码

打印结果:

objc[2699]: ############## 
objc[2699]: AUTORELEASE POOLS for thread 0x1000ebe00 
objc[2699]: 2 releases pending. 
objc[2699]: [0x10200e000] ................ PAGE (hot) (cold) 
objc[2699]: [0x10200e038] ################ POOL 0x10200e038 
objc[2699]: [0x10200e040] 0x1013658b0 LGPerson 
objc[2699]: ##############
复制代码
  • 只有p2对象压栈到自动释放池。

ARC模式,使用allocnewcopymutableCopy前缀开头的方法进行对象创建,不会加入到自动释放池。它们的空间开辟由开发者申请,释放也由开发者进行管理。

文章分类
iOS
文章标签