Principle of auto release pool page


The entry of all app s is a main function

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

It can be found that the entire iOS application is contained in an automatic release pool block
@autoreleasepool {} is essentially a structure:

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
  ~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
  void * atautoreleasepoolobj;
};

This structure will call objc during initialization_ Autoreleasepoolpush() method
When destructing, objc will be called_ Autoreleasepoolpop method
This shows that main function In actual work, it is like this:

int main(int argc, const char * argv[]) {
    {
        void * atautoreleasepoolobj = objc_autoreleasePoolPush();
        
        // do things you want
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}

 objc_ Autoreliasepoolpush and objc_ What is autoreleasepoolpop?

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

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

The above method seems to encapsulate the static methods push and pop corresponding to AutoreleasePoolPage.
The AutoreleasePoolPage structure is as follows:

class AutoreleasePoolPage {
    magic_t const magic;//AutoreleasePoolPage integrity check
    id *next;//The next address to store the autorelease object
    pthread_t const thread; //The thread on which AutoreleasePoolPage resides
    AutoreleasePoolPage * const parent;//Parent node
    AutoreleasePoolPage *child;//Child node
    uint32_t const depth;//Depth can also be understood as the position of the current page in the linked list
    uint32_t hiwat;
}

 

Each auto release pool is composed of a series of autorelease poolpages, and the size of each autorelease poolpage is 4096 bytes (hex 0x1000)

#define I386_PGBYTES 4096
#define PAGE_SIZE I386_PGBYTES

Automatic release pool is a two-way linked list composed of AutoreleasePoolPage

 

The structure of a single AutoreleasePoolPage is as follows:

The remaining variables of 0x81603 to 100817000 are used to automatically release the members of 100816 to 100817000.

1: The instance methods of begin() and end() help us quickly obtain the boundary addresses in the range of 0x100816038 ~ 0x100817000.
2: Next points to the next empty memory address. If an object is added to the address pointed to by next, it will move to the next empty memory address as shown in the figure below.
 

POOL_SENTINEL (sentinel object)

POOL_SENTINEL is just an alias for nil.

#define POOL_SENTINEL nil

Call objc at each auto release pool initialization_ When autorelease poolpush, a pool will be_ Sentinel pushes to the top of the stack of the automatic release pool and returns the POOL_SENTINEL object.  

int main(int argc, const char * argv[]) {
    {
    	//The atautoreleasepoolobj here is a POOL_SENTINEL
        void * atautoreleasepoolobj = objc_autoreleasePoolPush();
        
        // do whatever you want
        
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}

The above atautoreleasepoolobj is a POOL_SENTINEL.
When method objc_ When autoreleasepoolpop is called, a release message will be sent to the objects in the automatic release pool until the first POOL_SENTINEL:

objc_ Autoreliasepoolpush method

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

Here, the autoreasepoolpage:: push() method is called. hotPage refers to the autoreasepoolpage currently in use

static inline id *autoreleaseFast(id obj)
{
   AutoreleasePoolPage *page = hotPage();
   if (page && !page->full()) {//If there is a hotPage and the current page is not satisfied, add the object to the current stack
       return page->add(obj);
   } else if (page) {//There is a hotPage, but the current page is full. Find a full page or create a new page. Add the object to the new page
       return autoreleaseFullPage(obj, page);
   } else {//There is no hotPage. Create a hotPage and join it
       return autoreleaseNoPage(obj);
   }
}
  1. If there is a hotPage and the current page is not satisfied, directly call page - > Add (obj) to add the object to the automatic release pool.
    /Is a stack operation
    id *add(id obj) {
        id *ret = next;
        *next = obj;
        next++;
        return ret;
    }
    

  2. There is a hotPage but the current page is full. Find a full page or create a new page. Add the object to the new page. autoreleaseFullPage (called when the current page is full)
    static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page) {
    //Continue to traverse until an incomplete AutoreleasePoolPage is found. If it is not found in the end, a new AutoreleasePoolPage will be created
        do {
            if (page->child) 
            	page = page->child;
            else page = new AutoreleasePoolPage(page);
        } while (page->full());
    	
    	//Take the found or constructed page as hotPage, and then add obj
        setHotPage(page);
        return page->add(obj);
    }
    

If there is no hotPage, create a hotPage and add it. At this time, since there is no AutoreleasePoolPage in the memory, it is necessary to build a two-way linked list of the automatic release pool from scratch. As the first page table, the current page table has no parent pointer.

static id *autoreleaseNoPage(id obj) {
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);

    if (obj != POOL_SENTINEL) {
        page->add(POOL_SENTINEL);
    }

    return page->add(obj);
}

objc_autoreleasePoolPop

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

We usually pass in a sentinel object pool in this method_ Sentinel, release the object as shown in the following figure:

In general, autoreleasepool is a two-way linked list. Each node in the linked list is a stack. The stack stores a pointer to autoreleasepool, so the reference count of all objects in autoreleasepool will be + 1. Once autoreleasepool is out, if there is no pointer to the object, the reference count of the object will be - 1. Under ARC, xcode automatically adds autorelease pool to the code.
 

Keywords: iOS xcode objective-c

Added by treybraid on Sat, 05 Mar 2022 02:27:48 +0200