AutoreleasePool: Automatic release pool is an automatic release mechanism provided by OC. It has the feature of deferred release. When we create an object and add it to the automatic release pool, it will not be released immediately, but will wait until a runloop ends or the scope is out of {} or beyond [pool release].
(2) the alloc/init/new/copy/mutableCopy return objects are not autorelease objects
------in the createString()------
Hello
Hello, World!
Hello, World! Autorelease
------in the autoreleasepool------
Hello
(null)
Hello, World! Autorelease
------in the main()------
Hello
(null)
(null)
Copy the code
From the output above, we can see that the Hello from alloc always exists, while the Hello from alloc, World! The created object is released when it is out of the current scope. And if you have stringWithFormat creating an Autorelease object it will delay the release. Why is that?
1. Alloc/init/new/copy/mutableCopy objects created by not autorelease object, only in the current scope. 2. If a string contains less than or equal to eight characters, the system optimizes it into Tagged Pointer. Tagged Pointer stores data directly in the Pointer itself by setting a special flag on its last bit. Tagged Pointer is not a real object. When using it, be careful not to access its ISA variable directly.Copy the code
(3) AutoreleasePoolPage structure:
Before we learn how to do this, what is Autorelease EpoolPage
AutoreleasePoolPage is an AutoreleasePoolPage structure. Magic: verifies that the AutoreleasePoolPage structure is complete. Next: points to the top of the stack. Parent: indicates the parent node. Child: indicates the child node. Depth: indicates the depth of the list, that is, the number of nodes in the list.
A thread’s autoreleasepool is a pointer stack, and we add a POOL_BOUNDARY marker before each push and then add objects. When AutoReleasepool exits the stack, every object that is pushed after the sentinel object is released. The stack consists of a bidirectional linked list with pages as nodes, which increase or decrease as needed. The autoreleasepool thread stores references to the latest page. Draw a picture to understand:
(4) AutoreleasePool implementation:
How to add objects
/* @autoreleasepool */ __AtAutoreleasePool __autoreleasepool;
Copy the code
@autoreleasepool is used to declare a local variable of type __AtAutoreleasePool.
This is a syntactic sugar where the constructor is called at the start of a local variable and the destructor is called at the end of the scope. Let’s take a look at the objC source code for these two functions
void *
_objc_autoreleasePoolPush(void)
{
return objc_autoreleasePoolPush();
}
Copy the code
void *
objc_autoreleasePoolPush(void){
return AutoreleasePoolPage::push();
}
Copy the code
staticinlinevoid *push(a){
id *dest;
if (slowpath(DebugPoolAllocation)) {
// Each autorelease pool starts on a new pool page.// A new page is created at the start of each release pool
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
// Quickly add sentinel object nil
dest = autoreleaseFast(POOL_BOUNDARY);// The sentinel object
}
ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
Copy the code
AutoreleaseFast (POOL_BOUNDARY), POOL_BOUNDARY We call it the sentinel object, and if you look at its macro, it’s a nil.
# define POOL_BOUNDARY nilCopy the code
staticinline id *autoreleaseFast(id obj){
// Lists have Spaces
AutoreleasePoolPage *page = hotPage();// Go back to the latest pageif(page && ! page->full()) {//1. Add obj to obj and add page to objreturn page->add(obj);
} elseif (page) {Create a new page, add obj, child pointer to the newly created pagereturn autoreleaseFullPage(obj, page);
} else {// No page, create a page and add objreturnautoreleaseNoPage(obj); }}Copy the code
How do I add objC to a linked list by calling add(obj)
id *add(id obj)// Insert objc like the current list{ ASSERT(! full()); unprotect();// Remove the protection
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;// push obj to the top of the stack and relocate it
protect();
return ret;
}
Copy the code
Create a sentinel object POOL_BOUNDARY. After adding the object, point the next pointer to the last position.
How are objects freed
voidobjc_autoreleasePoolPop(void *ctxt){
AutoreleasePoolPage::pop(ctxt);
}
Copy the code
staticinlinevoidpop(void *token)// The token pointer points to the address at the top of the stack{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
} else {
page = pageForPointer(token);// Find the corresponding page from the address at the top of the stack
}
// Some code is omittedreturn popPage<false>(token, page, stop);
}
Copy the code
Get the current page and pop
staticvoidpopPage(void *token, AutoreleasePoolPage *page, id *stop){
if (allowDebug && PrintPoolHiwat) printHiwat();// Record the highest watermark mark
page->releaseUntil(stop);// Operate from the top of the stack and send a release message to the objects in the stack until the first sentinel object is encountered// memory: delete empty children// Delete the empty nodeif (allowDebug && DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} elseif(allowDebug && DebugMissingPools && page->empty() && ! page->parent) {// special case: delete everything for pop(top)// when debugging missing autorelease pools
page->kill();
setHotPage(nil);
} elseif (page->child) {
// hysteresis: keep one empty child if page is more than half fullif (page->lessThanHalfFull()) {
page->child->kill();
}
elseif(page->child->child) { page->child->child->kill(); }}}Copy the code
page->releaseUntil(stop)
voidreleaseUntil(id *stop){
// Not recursive: we don't want to blow out the stack // if a thread accumulates a stupendous amount of garbagewhile (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 itwhile (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if(obj ! = POOL_BOUNDARY) { objc_release(obj);// Exit if it is not a sentinel object
}
}
setHotPage(this);
#if DEBUG// we expect any children to be completely emptyfor (AutoreleasePoolPage *page = child; page; page = page->child) {
ASSERT(page->empty());
}
#endif
}
Copy the code
staticinline id autorelease(id obj){ ASSERT(obj); ASSERT(! obj->isTaggedPointer()); id *dest __unused = autoreleaseFast(obj);//ASSERT(! dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);return obj;
}
Copy the code
As with push, the key code for autoRelease is to call autoreleaseFast to add an object to the auto-release list stack, but push pushes a sentinel object, The autoRelease function pushes the object that needs to be added to the AutoReleasepool.
(v) Usage scenarios
Object as a function return value
+ (instancetype)object{
return [[MMPooLPerson alloc] init];
}
Copy the code
When an object as a function return value, because must follow the thought, who apply for release so should be returned before release, but if before returning to release, can cause wild pointer errors, but if you don’t release, it violates the principle of who apply for release, so you can use autorelease delay release characteristics, Make it an autorelease before returning, add it to the autorelease pool, make sure it can be returned, and the system will release it for us after a runloop.
2. Create a large number of objects temporarily
Always put the auto-release pool inside the for loop. If you do this outside, you will run out of memory because too many objects will not be released in time, and the program will exit unexpectedly
for (int i = 0; i<10000; i++) {
@autoreleasepool {
UIImageView *imegeV = [[UIImageView alloc]init];
imegeV.image = [UIImage imageNamed:@"efef"];
[self.view addSubview:imegeV]; }}Copy the code
Create a new thread
4. Tasks that run in the background for a long time
conclusion
@autoreleasepool
Autoreleasepool {} implicitly creates an autoreleasepool for each runloop, and all autoreleasepool autoreleasepool stacks. At the end of each runloop, the autoreleasePool at the top of the stack is destroyed and each object in it is released once (strictly speaking, you autorelease the object as many times as you do it, not necessarily once).
The system method that will help us automatically write to the auto release pool
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// This is surrounded by a local @autoreleasepool
}];
Copy the code
Automatic release of pool multi-layer nesting
Nested AutoReleasepool: An AutoRelease object is added to the nearest autoreleasepool. Multiple tiers of pools have multiple sentinels.
@autoreleasepool {// P1 is placed in the automatic release pool
Person *p1 = [[Person alloc]init];
@autoreleasepool {// P2 is placed in the automatic release poolPerson *p2 = [[Person alloc]init]; }}Copy the code
NSThread, NSRunLoop, and NSAutoreleasePool
1. Each thread, including the main thread, has its own NSRunLoop object, which is automatically created when needed. 2. Each Autoreleasepool corresponds to a thread. A thread can have multiple Autoreleasepool 3. Each thread maintains its own AutoReleasepool. The system automatically creates an AutoReleasepool before each event loop of the NSRunLoop object starts and drains at the end of the event loop