Resources to prepare

  • objcSource code download:Multiple versions of objC source code

Automatic release tank

An overview of the

AutoReleasePool: automatic release pool

An automatic memory reclamation mechanism in OC that delays the timing of variable release added to AutoreleasePool.

Simply put, when an object is created, the variable is normally released immediately when it is out of scope. If you add it to the autoreleasepool pool, the object will not be released immediately, but will be released after Runloop goes to sleep/out of autoreleasepool scope.

  • The Runloop corresponding to the main thread sleeps from the start of the program until it is loaded, waiting for user interaction to wake up the Runloop.

  • A Runloop is launched for each user interaction, which handles all user clicks, touches, etc.

  • When Runloop listens for interaction events, it creates an automatic release pool and adds any delayed release objects to the pool.

  • Before a full Runloop is completed, a Release message is sent to all objects in the auto-release pool and then the auto-release pool is destroyed.

structure

usecppFile to explore

Create a Mac project and, in main.m, automatically generate the following code:

#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"Hello, World!" ); } return 0; }Copy the code

Convert to CPP file:

clang -rewrite-objc main.m -o main.cpp
Copy the code

Open the CPP file and go to the main function:

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; 
}
Copy the code
  • Autoreleasepool is commented out, but the scope remains;

  • Scope generates code to declare the __AtAutoreleasePool type.

Find the __AtAutoreleasePool definition:

struct __AtAutoreleasePool { __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush(); } ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj); } void * atautoreleasepoolobj; };Copy the code
  • __AtAutoreleasePool is a structure containing constructors and destructors;

  • Struct declaration, trigger constructor, call objc_autoreleasePoolPush;

  • When the structure is out of scope space, the destructor is triggered and the objc_autoreleasePoolPop function is called.

Explore using assembly code

Open the project, set a breakpoint at autoreleasepool in the main function, and view the assembly code:

  • Call the objc_autoreleasePoolPush function;

  • Call the objc_autoreleasePoolPop function.

Enter the objc_autoreleasePoolPush function:

  • The source code comes fromlibobjcFramework.

The source code to explore

Open objC4-818.2 and find the objc_autoreleasePoolPush function:

void 
* objc_autoreleasePoolPush(void) { 
    return AutoreleasePoolPage::push(); 
}
Copy the code
  • callAutoreleasePoolPageUnder the namespacepushFunction.

AutoreleasePoolPage

Find the definition of AutoreleasePoolPage and first see a comment like this:

/*********************************************************************** Autorelease pool implementation A thread's autorelease pool is a stack of pointers. Each pointer is either an object torelease, or POOL_BOUNDARY which is an autorelease pool boundary. Each pointer is either an object to be released, 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. The pool token is a pointer to the POOL_BOUNDARY for that pool. When The pool is removed, The stack is divided into a key -linked list of pages. Pages are added and deleted as necessary. The stack is split into a double-linked list of pages. Thread-local storage points to the hot page, where newly autoreleased objects are stored. Thread local storage to hot page which store the object of the new automatic release * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /Copy the code

The following points can be learned from the comments:

  • Auto-release pools are related to threads;

  • The auto-release pool is a stack structure that stores Pointers;

  • The pointer is either an object to be released or POOL_BOUNDARY is an automatic release pool boundary.

    • The sentinel object is used when the automatic release pool will carry the objectpopWhen operating, you need to know where the boundaries are, otherwise you will destroy someone else’s memory space. The sentinel object, on the other hand, exists as a boundary marker.
  • The auto-free pool stack space is divided into a double-linked list of pages that can be added and removed:

    • Bidirectionally linked lists have both parent and child nodes in one page. Parent pages can be found forward, or child pages can be found backward.
  • Thread local storage points to hot pages that store the newly automatically freed objects:

    • Stack principle, advanced after out, can be understood as the last page is a hot page. The object in it was finally takenpushAnd the first to bepop.

AutoreleasePoolPage inherited from 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; . }Copy the code

AutoreleasePoolPageData

Find the definition of 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) { } };Copy the code

The structure contains the following member variables:

  • Magic: used to check whether the structure of AutoreleasePoolPage is complete.

  • Next: points to the next location of the newly added Autoreleased object and executes begin() when initialized;

  • Thread: points to the current thread.

  • Parent: points to the parent node, the parent value of the first node is nil;

  • Child: points to a child node, and the child value of the last node is nil;

  • Depth: indicates the depth. It starts at 0 and increases by 1.

  • Hiwat: indicates the mark of the maximum amount of high water mark.

Print structure

Set up the test project and close ARC mode:

Open the main.m file and write the following code:

extern void _objc_autoreleasePoolPrint(void); 

int main(int argc, const char * argv[]) {
    @autoreleasepool { 
        NSObject *objc = [[[NSObject alloc] init] autorelease]; 
        
        _objc_autoreleasePoolPrint(); 
    }
    
    return 0; 
}
Copy the code
  • Import the _objc_autoreleasePoolPrint function to print the structure of the automatic release pool.

  • Create an NSObject instance object and add it to the automatic release pool.

  • Call the _objc_autoreleasePoolPrint function to print the structure.

Output the following:

############## 
AUTORELEASE POOLS for thread 0x1000ebe00 
2 releases pending. 
[0x10700b000] ................ PAGE (hot) (cold) 
[0x10700b038] ################ POOL 0x10700b038 
[0x10700b040] 0x100705f60 NSObject 
##############
Copy the code
  • Prints out the current auto-release pool thread

  • There are two objects that need to be released

  • Current Page information, 56 bytes. Because there is only one page, both the cold page and the hot page

  • Sentinel object POOL

  • NSObject object

_objc_autoreleasePoolPrint

The official function used to debug print the contents of the auto-release pool:

void 
_objc_autoreleasePoolPrint(void) { 
    AutoreleasePoolPage::printAll();
}
Copy the code

Let’s go to 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 };Copy the code
  • According to the structure of automatic release pool, traversal through bidirectional linked listpage, read in sequencepageAnd print the contents of.

Object pressure stack

Enter the objc_autoreleasePoolPush function:

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

Enter the push function in the AutoreleasePoolPage namespace:

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; 
}
Copy the code
  • DebugPoolAllocation: stops when the automatic release pool is ejected in sequence and allows the heap debugger to track the automatic release pool;

  • If not, call autoreleaseNewPage and start with a new pool page.

  • Otherwise, the autoreleaseFast function is called to push the sentinel object onto the stack.

autoreleaseFast

Enter the autoreleaseFast function:

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);
    } 
}
Copy the code
  • If a page exists and is not full, call add.

  • If a page exists but the storage is full, call the autoreleaseFullPage function.

  • Otherwise, there is no page and the autoreleaseNoPage function is called.

autoreleaseNoPage

Enter the autoreleaseNoPage function:

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); }Copy the code
  • Call the AutoreleasePoolPage constructor to create a new page.

  • Set to hot page;

  • PushExtraBoundary = YES, the sentry object is pushed;

  • Object pushdown.

Enter the AutoreleasePoolPage constructor:

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(); }Copy the code
  • Initialize with the parent class AutoreleasePoolPageData;

  • Begin: Obtains the start position of object pressing.

  • Objc_thread_self: gets the current thread through TLS;

  • Linked bidirectional lists.

Enter the begin function:

id * begin() { 
     return (id *) ((uint8_t *)this+sizeof(*this));
 }
Copy the code
  • Sizeof (*this) : The size depends on the member variable in its own structure;

  • Returns the actual starting address of the object that can be pushed, below the member variable.

Enter the AutoreleasePoolPageData definition:

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; . };Copy the code
  • Magic: 16 bytes;

  • Next, Thread, parent, child: 8 bytes each;

  • Depth and hiwat: 4 bytes each;

  • A total of 56 bytes.

Enter the definition of 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 };Copy the code
  • The static members are stored in the static area and do not occupy the size of the structure.

  • The 16 bytes come from the Uint32_t array.

Enter the objc_thread_self function:

static inline pthread_t objc_thread_self() {
    return (pthread_t)tls_get_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF);
}
Copy the code
  • throughtlsGets the current thread.

autoreleaseFullPage

Enter the autoreleaseFullPage function:

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);
}
Copy the code
  • Iterate through the list to find the last blank child page;

  • Create a new page for it;

  • Set to hot page;

  • Add objects.

The following data structures are formed:

add

Enter the add function:

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; }Copy the code
  • *next++ for memory translation;

  • Push objects onto the stack.

autoreleaseNewPage

Enter the autoreleaseNewPage function:

id 
*autoreleaseNewPage(id obj) { 
    AutoreleasePoolPage *page = hotPage(); 
    if (page) 
        return autoreleaseFullPage(obj, page); 
    else 
        return autoreleaseNoPage(obj);
}
Copy the code
  • Get hot page;

  • If yes, call autoreleaseFullPage;

  • Otherwise, there is no page and the autoreleaseNoPage function is called.

autorelease

Enter the objc_autorelease function:

id 
objc_autorelease(id obj) {
    if (obj->isTaggedPointerOrNil()) return obj; 
    return obj->autorelease(); 
}
Copy the code
  • The current object is TaggedPointer or nil and returns directly;

  • Otherwise, the object’s autorelease function is called.

Enter the autorelease function:

inline id objc_object::autorelease() { ASSERT(! isTaggedPointer()); if (fastpath(! ISA()->hasCustomRR())) { return rootAutorelease(); } return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(autorelease)); }Copy the code
  • If it is a custom class, callrootAutoreleaseFunction.

Enter rootAutorelease – > rootAutorelease2 – > AutoreleasePoolPage: : autorelease function:

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; }Copy the code
  • callautoreleaseFastFunction, object push.

Page pool capacity

The auto-release pool stores objects in paging mode, because the value of the object will affect the current page in the process of frequent pressing and unloading, and does not affect the entire auto-release pool.

And automatically release pool paging management, the address before each page can be discontinuous, they can use bidirectional linked lists to find the parent page and child page. If all objects are stored on one page, each capacity expansion is tedious and time-consuming to ensure the continuity of addresses:

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;
}
Copy the code

Print result:

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]: ##############
Copy the code
  • 505 NSObject objects are looped into the automatic release pool. When 504 objects are stored, the pool page is full. The 505th object creates a new pool page store;

  • Size of a page: 504 * 8 = 4032, plus 56 bytes of member variables and 8 bytes of sentinel object, total 4096 bytes;

  • Each page contains 56 bytes of member variables;

  • An auto-release pool pushes only one sentinel object.

View in the source code:

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 

... 

}
Copy the code

Go to the definition of PAGE_MIN_SIZE:

#define PAGE_MIN_SHIFT     12 
#define PAGE_MIN_SIZE      (1 << PAGE_MIN_SHIFT)
Copy the code
  • 1Shift to the left12Bit, equivalent to2 to the 12th is 4096.

The object out of the stack

Enter the objc_autoreleasePoolPop function:

void 
objc_autoreleasePoolPop(void *ctxt) { 
    AutoreleasePoolPage::pop(ctxt);
}
Copy the code

Enter the pop function:

static inline void pop(void *token) { AutoreleasePoolPage *page; id *stop; If (token == (void*)EMPTY_POOL_PLACEHOLDER) {// Put the cap on the placeholder hotPage(); if (! {// Pool was never used. Clear the placeholder. // Pool was never used. } // Pool pages remain remain of allocated tokens for re-use as usual. Loop through the bidirectional linked list to find the coldest page page = coldPage(); Token = page->begin(); } else {page = pageForPointer(token); } // assign to stop stop = (id *)token; // The current position is not a sentinel object 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 // The beginning of the cold page may not be POOL_BOUNDARY: //1. Pop up the top-level pool, } 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); }Copy the code

popPage

Enter the popPage function:

static void popPage(void *token, AutoreleasePoolPage *page, id *stop) { if (allowDebug && PrintPoolHiwat) printHiwat(); Page ->releaseUntil(stop); // memory: delete empty children if (allowDebug && DebugPoolAllocation && page->empty()) { // special case: Delete everything during page-per-pool debugging // Special case: Delete all contents during page-per-pool debugging // Obtain the parent page AutoreleasePoolPage *parent = page->parent; // Destroy current page page->kill(); // Set the parent page to the hot page setHotPage(parent); } else if (allowDebug && DebugMissingPools && page->empty() && ! page->parent) { // special case: Delete everything for pop(top) // when debugging missing autorelease pools // setHotPage tag to nil setHotPage(nil); } else if (page->child) { // hysteresis: Keep one empty child if the page is more than half full If (page->lessThanHalfFull()) {page->child->kill(); } else if (page->child->child) { page->child->child->kill(); }}}Copy the code

releaseUntil

Enter the releaseUntil function:

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, AutoreleasePoolPage *page = hotPage(); // fixme I think this `while` can be `if`, While (page->empty()) {// Get the parent page = page->parent; // mark as hot page 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; Obj = *--page->next; // grab these before memset #else #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 // release objc_release(obj); SetHotPage (this); #if DEBUG // we expect any children to be completely empty for (AutoreleasePoolPage *page = child; page; page = page->child) { ASSERT(page->empty()); } #endif }Copy the code

kill

Enter the kill function:

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) {// Set subpages to nil page->unprotect(); page->child = nil; page->protect(); } // Delete deathptr; } while (deathptr! = this); }Copy the code

Use nested

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; 
}
Copy the code

Print result:

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]: ##############
Copy the code
  • Automatic release pool can be used in nesting, storage structure and normal use of the same;

  • The child release pool has its own sentinel object.

ARCmodel

Set the project to ARC mode:

Create LGPerson class:

#import <Foundation/Foundation.h> 

@interface LGPerson : NSObject

+ (LGPerson *)person; 

@end 


@implementation LGPerson

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

@end
Copy the code

In the main function, write the following:

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; 
}
Copy the code

Print result:

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]: ##############
Copy the code
  • onlyp2Objects are pushed to the automatic release pool.

ARC mode, using methods starting with alloc, new, copy, mutableCopy prefix for object creation, will not be added to the auto release pool. Their space creation is requested by the developer, and their release is managed by the developer.