The main() function of iOS applications is familiar to all of us. There is an autoreleasepool pool at the entrance of the function. Today we will start to explore what autoreleasepool is 😏

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello, AutoreleasePool!");
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}Copy the code

With clang converted to C++ code to implement the main. CPP file, take a peek at the main() function:

int main(int argc, char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; NSLog((NSString *)&__NSConstantStringImpl__var_folders__6_s9x0n6313d99yqk5pltzp6ym0000gn_T_main_301f30_mi_0); return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")))); }}Copy the code

@autoreleasepool corresponds to a variable of type __AtAutoreleasePool. The __AtAutoreleasePool definition found in the main. CPP file is as follows:

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

__AtAutoreleasePool is a C++ structure, which is similar to the concept of a class in iOS. __AtAutoreleasePool() and ~__AtAutoreleasePool() are called constructors and destructors. They are called when the structure is created and destroyed. The function is similar to iOS – (void)init – (void)dealloc. So the __autoreleasepool object is called objc_autoreleasePoolPush() as soon as it is created; To destroy objects, call objc_autoreleasePoolPop(AtAutoReleasepoolobj). With that in mind, we follow objc_autoreleasePoolPush(); objc_autoreleasePoolPush(); The implementation of these two functions can be seen below (from where? Available from Apple’s open source objC source).

I will source the relevant call function excerpted as follows (not excerpted how to call “read source” 🙄) :

void *_objc_autoreleasePoolPush(void) { return objc_autoreleasePoolPush(); } void *objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push(); } static inline void *push() { id *dest; if (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; } id *autoreleaseNewPage(id obj) { AutoreleasePoolPage *page = hotPage(); if (page) return autoreleaseFullPage(obj, page); else return autoreleaseNoPage(obj); } 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

AutoreleasePoolPage has 7 members and some internal functions (omitted) : AutoreleasePoolPage has 7 members and some internal functions (omitted)

AutoreleasePoolPage.png

  • Each AutoreleasePoolPage object takes up 4096 bytes of memory, except for its internal member variables and the remaining space for the address of the AutoRelease object.
  • All AutoreleasePoolPage objects are connected together in a bidirectional linked list.

AutoreleasePoolPage Relationships between objects. PNG

AutoreleasePool is expanded in autoreleasePoolPage units because each autoreleasePoolPage has only 4096 bytes. If there are too many objects in the auto-release pool, paging (n autoreleasePoolPage) storage is required. To enable an autoreleaseNewPage(POOL_BOUNDARY) in push(), execute autoreleaseNewPage(POOL_BOUNDARY). , pushing a POOL_BOUNDARY onto the pool automatically releases the pool and returns the POOL_BOUNDARY pointer in the pool. (POOL_BOUNDARY is a system-defined macro that is defined as # define POOL_BOUNDARY nil). Call pop(void CTXT) and pass in the POOL_BOUNDARY address. A release message will be sent from the last object to be pushed until the POOL_BOUNDARY is reached. The auto-release pool can be nested in multiple layers, like the following (demo running MRC*) :

Multilevel nested automatic release pool. PNG

POOL_BOUNDARY is also called by push() several times after the nesting, and each time a new pointer to the POOL_BOUNDARY is returned. Each push() has its corresponding pop(void * CTXT) function.

PNG is automatically released after multiple @autoreleasepool tiers

In demo, the @autoreleasepool layer 4 is selected. The system function void _objc_autoreleasePoolPrint(void) is used. You can get the state of the release pool. __NSArray and __NSSetI are system objects, the last TestObject is created by ourselves, and the middle four POOL objects are created by calling push() four times. A POOL_BOUNDARY is pressed into the object in the pool after each push().

Since objects in the pool are released when the pool ends, the @Autoreleasepool function is always running as long as the program does not exit. Obviously not dependent on automatic release pools in main().