Explorando los principios subyacentes de iOS ----- administración de memoria (3)

preparación de recursos

piscina de liberación automática

Visión de conjunto

AutoReleasePool: piscina de liberación automática

OCUn mecanismo automático de recuperación de memoria en , que retrasa el momento de agregar AutoreleasePoolvariables en .release

En pocas palabras, cuando se crea un objeto, en circunstancias normales, las variables quedan fuera del alcance inmediatamente release. Si se agrega al grupo de liberación automática, el objeto no se liberará de inmediato, pero se liberará después de runloopdormir/salir autoreleasepooldel alcance.

imagen.png

  • Desde el inicio del programa hasta la finalización de la carga, el subproceso principal correspondiente Runloopestará en un estado inactivo, esperando que se despierte la interacción del usuario Runloop;

  • Cada interacción del usuario se iniciará una vez Runlooppara manejar todos los clics, toques y otros eventos del usuario;

  • RunloopDespués de escuchar el evento de interacción, se creará un grupo de lanzamiento automático y todos los objetos de lanzamiento retrasado se agregarán al grupo de lanzamiento automático;

  • Antes de que finalice por completo Runloop, se envía un mensaje a todos los objetos del grupo de liberación automática releasey, a continuación, se destruye el grupo de liberación automática.

estructura

cppExplorar con archivos

Cree un Macproyecto, en main.mel que se genere automáticamente el siguiente código:

#import <Foundation/Foundation.h> 

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

cppArchivo generado por conversión :

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

Abra el cpparchivo y acceda a la mainfunción:

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; 
}
复制代码
  • autoreleasepoolestá comentado, pero el alcance sigue ahí;

  • Scope genera __AtAutoreleasePoolcódigo para declaraciones de tipos.

Encuentra la __AtAutoreleasePooldefinición:

struct __AtAutoreleasePool { 
    __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} 
    ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} 
    void * atautoreleasepoolobj; 
};
复制代码
  • __AtAutoreleasePooles una estructura, incluyendo constructor y destructor;

  • Declaración de estructura, constructor de disparadores, objc_autoreleasePoolPushfunción de llamada;

  • Cuando la estructura sale del ámbito de aplicación, se activa el destructor y se llama a la función objc_autoreleasePoolPop.

Explorar con código ensamblador

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

imagen.png

  • 调用objc_autoreleasePoolPush函数;

  • 调用objc_autoreleasePoolPop函数。

进入objc_autoreleasePoolPush函数:

imagen.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模式:

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

  • 对其进行创建新页;

  • 设置为热页面;

  • 添加对象。

形成以下数据结构:

imagen.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模式:

imagen.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对象压栈到自动释放池。

ARCMode, use los métodos que comienzan con alloc, new, copyy mutableCopyel prefijo para crear objetos, y no se agregarán al grupo de lanzamiento automático. El desarrollador solicita el desarrollo de su espacio, y el desarrollador también administra el lanzamiento.

Supongo que te gusta

Origin juejin.im/post/7007703060046675982
Recomendado
Clasificación