About the automatic release of the pool explanation and introduction

Source: Internet
Author: User

Because memory management in OBJECTIVE-C is a relatively big topic, it will be divided into two articles to analyze some mechanisms in memory management, part of the analysis of the automatic release pool and Autorelease method, the other part analysis retain, release The implementation of the method and the automatic reference count.

Write in front

This article introduces the objective-c in the source level, as well as the implementation of the autorelease of the method.

Start with the main function

The main function can be said to be a very obscure function throughout iOS development, which is well hidden in the supporting Files folder and is the gateway to the entire iOS app.

The contents of the main.m file are: int main (int argc, char * argv[]) {@autoreleasepool {return uiapplicationmain (argc, argv, Nil, NSSTRINGF Romclass ([Appdelegate class]);}}

In this @autoreleasepool block contains only one line of code, this line of code will all the events, messages all to uiapplication to deal with, but this is not the focus of this article.

It is important to note that the entire IOS app is included in an auto-release pool block.

@autoreleasepool

What the hell is @autoreleasepool? We use CLANG-REWRITE-OBJC main.m in the command line to have the compiler rewrite this file:

$ CLANG-REWRITE-OBJC MAIN.M

After a lot of warnings have been generated, there is a main.cpp file in the current directory

This removes the other useless code from the main function.

In this file, there is a very strange __atautoreleasepool structure, the previous comment is written to/* @autoreleasepool */. This means that @autoreleasepool {} is converted to a __atautoreleasepool struct:

{__atautoreleasepool __autoreleasepool;}

To figure out what this line of code means, we'll look for a struct named __atautoreleasepool in main.cpp:

struct __atautoreleasepool {__atautoreleasepool () {atautoreleasepoolobj = Objc_autoreleasepoolpush ();} ~__atautoreleasepool () {objc_autoreleasepoolpop (atautoreleasepoolobj);} void * atautoreleasepoolobj;};

This architecture invokes the Objc_autoreleasepoolpush () method at initialization time and invokes the Objc_autoreleasepoolpop method at destructor time.

This shows that our main function actually works like this:

int main (int argc, const char * argv[]) {{void * atautoreleasepoolobj = Objc_autoreleasepoolpush ();//do whatever you want Objc_autoreleasepoolpop (atautoreleasepoolobj);} return 0;}

@autoreleasepool just help us write less of these two lines of code, make the code look more beautiful, and then analyze the implementation of the auto-free pool based on the two methods described above.

What is Autoreleasepool?

This section begins the analysis of the implementation of methods Objc_autoreleasepoolpush and Objc_autoreleasepoolpop:

void *objc_autoreleasepoolpush (void) {return autoreleasepoolpage::p ush ();} void Objc_autoreleasepoolpop (void *ctxt) {autoreleasepoolpage::p op (ctxt);}

The above method appears to be the encapsulation of autoreleasepoolpage corresponding to static method push and pop.

This section parses the contents of the code in the following order:

Structure of the Autoreleasepoolpage

Objc_autoreleasepoolpush method

Objc_autoreleasepoolpop method

Structure of the Autoreleasepoolpage

Autoreleasepoolpage is a class in C + +:

It is defined in the NSOBJECT.MM as follows:

Class Autoreleasepoolpage {magic_t const magic;id *next;pthread_t const thread; Autoreleasepoolpage * Const parent; Autoreleasepoolpage *child;uint32_t const depth;uint32_t Hiwat;};
    • Magic is used to verify the integrity of the current Autoreleasepoolpage

    • Thread holds the current page.

Each auto-release pool is made up of a series of autoreleasepoolpage, and each autoreleasepoolpage size is 4096 bytes (16 binary 0x1000)

#define I386_pgbytes 4096#define page_size i386_pgbytes

Doubly linked list

The autoreleasepoolpage in the auto-free pool is connected in the form of a doubly linked list:

Parent and child are pointers to constructing a doubly linked list.

Automatically frees stacks in the pool

If one of our autoreleasepoolpage is initialized in memory 0x100816000 ~ 0x100817000, its structure in memory is as follows:

There are the autoreleasepoolpage bits used to store the member variables of the 0x100816038, and the remainder of the 0x100817000 is used to store the objects added to the auto-release pool.

The two class instance methods of Begin () and end () help us quickly get to the boundary address of the range 0x100816038 ~ 0x100817000.

Next points to the next empty memory address, and if the address pointed to by next joins an object, it moves to the next empty memory address as shown:

About Hiwat and depth are not covered in the article because they do not affect the implementation of the entire auto-release pool, nor are they in the call stack of critical methods.

Pool_sentinel (Sentinel Object)

When you get here, you may want to know what Pool_sentinel is and why it is in the stack.

First answer the first question: Pool_sentinel is just a nil alias.

#define Pool_sentinel Nil

When each auto-release pool initializes the call to Objc_autoreleasepoolpush, a pool_sentinel is pushed to the top of the auto-release pool, and the Pool_sentinel Sentinel object is returned.

int main (int argc, const char * argv[]) {{void * atautoreleasepoolobj = Objc_autoreleasepoolpush ();//do whatever you want Objc_autoreleasepoolpop (atautoreleasepoolobj);} return 0;}

The atautoreleasepoolobj above is a pool_sentinel.

When the method Objc_autoreleasepoolpop is called, the release message is sent to the object in the auto-free pool until the first Pool_sentinel:

Objc_autoreleasepoolpush method

With Pool_sentinel, let's revisit the Objc_autoreleasepoolpush method:

void *objc_autoreleasepoolpush (void) {return autoreleasepoolpage::p ush ();}

It calls Autoreleasepoolpage's class method push, which is also very simple:

static inline void *push () {return autoreleasefast (Pool_sentinel);}

Here you will enter a more critical method Autoreleasefast, and pass in the Sentinel object Pool_sentinel:

Static inline ID *autoreleasefast (id obj) {autoreleasepoolpage *page = Hotpage (); if (page &&!page->full ()) {RE Turn page->add (obj);} else if (page) {return autoreleasefullpage (obj, page);} else {return autoreleasenopage (obj);}}

The above method is divided into three cases to choose different code execution:

    • have hotpage and current page dissatisfaction

Call the Page->add (obj) method to add an object to the Autoreleasepoolpage stack

    • Has hotpage and the current page is full

Call Autoreleasefullpage to initialize a new page

Call the Page->add (obj) method to add an object to the Autoreleasepoolpage stack

    • No hotpage

Call Autoreleasenopage to create a hotpage

Call the Page->add (obj) method to add an object to the Autoreleasepoolpage stack

The final call to Page->add (obj) adds the object to the auto-free pool.

Hotpage can be understood as the autoreleasepoolpage that is currently being used.

Page->add adding objects

ID *add (ID obj) adds the object to the auto-free pool page:

ID *add (ID obj) {id *ret = Next;*next = Obj;next++;return ret;}

The author has dealt with this method, and it is more convenient to understand.

This method is actually a stack operation, adding the object to the Autoreleasepoolpage and then moving the pointer over the top of the stack.

Autoreleasefullpage (current hotpage full)

The autoreleasefullpage will be called when the current hotpage is full:

Static ID *autoreleasefullpage (ID obj, autoreleasepoolpage *page) {do {if (page->child) page = Page->child;else page = new Autoreleasepoolpage (page);} while (Page->full ()); Sethotpage (page); return Page->add (obj);}

It traverses the entire doubly linked list from the page that is passed in, until:

    • Find a autoreleasepoolpage that is not full

    • Use the constructor to pass in the parent to create a new autoreleasepoolpage

After finding a autoreleasepoolpage that can be used, the page is marked as Hotpage and then the Page->add method that was analyzed above is mobilized to add the object.

Autoreleasenopage (no hotpage)

If Hotpage is not present in the current memory, the Autoreleasenopage method is called to initialize a

Autoreleasepoolpage:static ID *autoreleasenopage (id obj) {autoreleasepoolpage *page = new Autoreleasepoolpage (nil); Sethotpage (page), if (obj! = Pool_sentinel) {page->add (pool_sentinel);} return Page->add (obj);

Since Autoreleasepoolpage is not present in memory, it is necessary to build a doubly linked list of the auto-free pool from scratch, that is, the new autoreleasepoolpage does not have a parent pointer.

After initialization, the current page is marked as Hotpage, and a Pool_sentinel object is added to the page to ensure that no exception occurs when the pop is called.

Finally, add obj to the auto-release pool.

Objc_autoreleasepoolpop method

Again, look back at the Objc_autoreleasepoolpop method mentioned above:

void Objc_autoreleasepoolpop (void *ctxt) {autoreleasepoolpage::p op (ctxt);}

It seems to be possible to pass in any of the pointers, but there are no examples of incoming objects being found throughout the project. However, it is also possible to pass in the other pointers in this method, releasing the auto-free pool to the appropriate location.

We typically pass in this method a Sentinel object Pool_sentinel, such as releasing an object:

Testing of Objc_autoreleasepoolpop behavior

Before continuing the analysis of this method, do a small test, passing the non-Sentinel object in Objc_autoreleasepoolpop, and test the behavior of this method.

The following is the source code in the main.m file:

#import int main (int argc, const char * argv[]) {@autoreleasepool {nsstring *s = @ "Draveness"; [ S stringbyappendingstring:@ "-suffix"];} return 0;}

Make a breakpoint in this line of code, as this will call the Autorelease method and add the string to the auto-free pool:

When the code runs here, the contents of the stack in the current hotpage are printed through LLDB:

    • Gets the current hotpage through the static method

    • Print content in a autoreleasepoolpage

    • Prints the contents of the current next pointer, as well as the previous content, when 2 has reached the Begin () position

    • Automatically frees content in the pool using print () and printall ()

Then pass the pointer of the string @ "Draveness-suffix" into the pop method to test whether the pop method can pass in the non-Sentinel parameter.

When printing the contents of the current Autoreleasepoolpage again, the string no longer exists, which means it is possible to pass in the non-Sentinel parameter to the Pop method, except that we generally do not pass in the non-Sentinel object.

Let's go back to the analysis of the Objc_autoreleasepoolpop method, which is autoreleasepoolpage: the invocation of the:p op method:

static inline void pop (void *token) {autoreleasepoolpage *page = pageforpointer (token); ID *stop = (ID *) Token;page->rel Easeuntil (stop), if (Page->child) {if (Page->lessthanhalffull ()) {Page->child->kill ();} else if (page-> Child->child) {Page->child->child->kill ();}}}

In this method, a lot of extraneous code is removed and the format is adjusted.

This static method does a total of three things:

    • Use Pageforpointer to get the autoreleasepoolpage of the current token

    • Call the Releaseuntil method to release the object in the stack until the stop

    • Call child's Kill method

It's not clear to me why I'm going to kill different child pages based on the different states of the current page.

if (Page->lessthanhalffull ()) {Page->child->kill ();} else if (page->child->child) {page->child- >child->kill ();}

Pageforpointer Get Autoreleasepoolpage

The Pageforpointer method is to get the first address of the page where the current pointer is located, mainly through the operation of the memory address:

Static autoreleasepoolpage *pageforpointer (const void *p) {return Pageforpointer ((uintptr_t) p);} Static Autoreleasepoolpage *pageforpointer (uintptr_t p) {autoreleasepoolpage *result;uintptr_t offset = p% SIZE;assert ( Offset >= sizeof (autoreleasepoolpage)); result = (Autoreleasepoolpage *) (P-offset); Result->fastcheck (); return Result;}

Take the pointer to the size of the page, which is 4096, to get the current pointer offset, because all the autoreleasepoolpage are aligned in memory:

p = 0x100816048

P% SIZE = 0x48

result = 0x100816000

And the last method called Fastcheck () is used to check whether the current result is a autoreleasepoolpage.

Check whether a member in the magic_t struct is 0xa1a1a1a1.

Releaseuntil Releasing objects

The Releaseuntil method is implemented as follows:

void Releaseuntil (ID *stop) {while (This->next! = stop) {autoreleasepoolpage *page = Hotpage (); while (Page->empty () {page = page->parent;sethotpage (page);} Page->unprotect (); id obj = *--page->next;memset ((void*) Page->next, SCRIBBLE, sizeof (*page->next));p age- >protect (); if (obj! = Pool_sentinel) {objc_release (obj);}} Sethotpage (this);}

Its implementation is still easy, using a while loop to continuously release the contents of Autoreleasepoolpage until next points to stop.

Use Memset to set the contents of the memory to SCRIBBLE, and then use Objc_release to release the object.

Kill () method

Here, there is no parsing method but only kill, and it will delete the current page and all the sub-pages:

void Kill () {autoreleasepoolpage *page = this;while (page->child) page = page->child; Autoreleasepoolpage *deathptr;do {deathptr = Page;page = page->parent;if (page) {page->unprotect ();p age-> Child = Nil;page->protect ();} Delete deathptr;} while (deathptr! = this);}

Autorelease method

We have a good understanding of the auto-release pool life cycle, and the last thing we need to know is the implementation of the Autorelease method, first look at the call stack of the method:

-[NSObject autorelease]└──id objc_object::rootautorelease () └──id objc_object::rootautorelease2 () └──static ID Autoreleasepoolpage::autorelease (ID obj) └──static ID autoreleasepoolpage::autoreleasefast (id obj) ├──id *add (id obj) ├──static ID *autoreleasefullpage (id obj, autoreleasepoolpage *page) │├──autoreleasepoolpage (AutoreleasePoolPage *new Parent) │└──id *add (ID obj) └──static ID *autoreleasenopage (ID obj) ├──autoreleasepoolpage (autoreleasepoolpage *newPare NT) └──id *add (id obj)

In the call stack of the Autorelease method, the Autoreleasefast method mentioned above will eventually be called to add the current object to Autoreleasepoolpage.

The implementation of these methods in this section is very easy, with only a few parameters checked and finally the Autoreleasefast method called:

Inline ID objc_object::rootautorelease () {if (Istaggedpointer ()) return (ID) this;if (prepareoptimizedreturn ( RETURNATPLUS1)) return (ID) this;return rootAutorelease2 ();} __attribute__ ((noinline,used)) ID Objc_object::rootautorelease2 () {return autoreleasepoolpage::autorelease (ID) this);} Static inline ID autorelease (ID obj) {id *dest __unused = autoreleasefast (obj); return obj;}

Since the implementation of the Autoreleasefast method has been analyzed above, there is no more to say here.

Summary

The implementation of the entire automatic release pool Autoreleasepool and the Autorelease method have been analyzed, let's review some of the article:

The auto-free pool is implemented by Autoreleasepoolpage in a doubly linked list

When an object calls the Autorelease method, the object is added to the autoreleasepoolpage stack

Call Autoreleasepoolpage::p op method sends a release message to an object in the stack

Resources

Gain insight into the source code analysis of each third party: Ios-source-code-analyze

What is Autoreleasepool? -Objective-c

Using autorelease Pool Blocks

NSAutoreleasePool

The autorelease behind the shady

About the automatic release of the pool explanation and introduction

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.