1. AutoreleasePool profile

1.1 the ARC and Autorelease

ARC inserts release/autorelease statements for objects at compile time where appropriate. Objects that require an Autorelease are thrown into the AutoreleasePool, postponing the release until the AutoreleasePool is destroyed at the end of the current RunLoop.

1.2 Objects for Autorelease

  • Alloc, copy, mutableCopy, new object: release, released when the scope ends
  • Objects of [NSMutableArray Array] and [NSObject Object] : AutoRelease. AutoreleasePool is released only when the AutoreleasePool is destroyed

1.3 the thread – RunLoop – AutoreleasePool

The observer of the thread observes that RunLoop is about to start and sleep/end, and calls autoreleasePool push and POP; Each thread has a RunLoop, which is created as a lazy load; Each thread maintains its own AutoreleasePool stack;

2. AutoreleasePool application

2.1 Use AutoreleasePool to reduce peak memory usage when creating a large number of temporary objects in a for loop

for (int i = 0; i < 100000000; i ++) {
    @autoreleasepool {
        NSString * str = [NSString stringWithFormat:@"AutoReleasePool"];
        NSString*tempstr = str; }}Copy the code

The block enumerator is internally optimized for the for loop and contains autoreleasePool:

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // This is surrounded by a local @autoreleasepool
}];
Copy the code

3. Basic implementation of AutoreleasePool

AutoreleasePool is composed of several AutoreleasepoolPages in the form of a bidirectional linked list.

3.1 AutoreleasePoolPage

AutoreleasepoolPage stores each AutoRelease object (from the lowest address to the highest address) in a stacked manner, and each page occupies one page of virtual memory.

class AutoreleasePoolPage { 
#   define POOL_BOUNDARY nil // The sentinel object
    static size_t const COUNT = SIZE / sizeof(id); // Number of objects
    id *next; // Points to the next location of the newly added autoreleased object on the top of the stack, initialized to 'begin()'
    pthread_t const thread; // point to the current thread;
    AutoreleasePoolPage * const parent; // point to the parent
    AutoreleasePoolPage *child; // point to the child
    uint32_t const depth; / / depth. }Copy the code

3.2 Push

Insert the sentinel object POOL_BOUNDARY and call autoreleaseFast(obj).

AutoreleaseFast (obj) : Pushes the current page if the current page exists and is not full. If there is no current page or the current page is full, create a new page and push it to the page, and connect the bidirectional linked list pointer.

Using @Autoreleasepool {} essentially calls the push() method;

3.3 Autorelease

Insert objects that require an AutoRelease, calling autoreleaseFast(obj) just like push;

3.4 Pop

The external loop iterates over the Autoreleased object, releasing each object until the sentinel object POOL_BOUNDARY is identified and a new hotPage is configured.

Nested AutoreleasePool does not affect each other, but the same variable is released only once.