Resources to prepare
objc
Source 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
usecpp
File 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 from
libobjc
Framework.
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
- call
AutoreleasePoolPage
Under the namespacepush
Function.
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 object
pop
When 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 sentinel object is used when the automatic release pool will carry the object
-
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 taken
push
And the first to bepop
.
- Stack principle, advanced after out, can be understood as the last page is a hot page. The object in it was finally taken
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 list
page
, read in sequencepage
And 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
- through
tls
Gets 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, call
rootAutorelease
Function.
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
- call
autoreleaseFast
Function, 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
1
Shift to the left12
Bit, 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.
ARC
model
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
- only
p2
Objects 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.