preface

The chapters in memory management also have an AutoreleasePool, which is a memory reclamation mechanism where objects placed in the AutoreleasePool are released late. So what is this auto-release pool and what are its features? We’ll take a closer look at it below

Structure analysis

  • Autoreleasepool (@autoreleasepool) in main.m



    In C++ source, @autoreleasepool is an __autoreleasepool constructor. A search for AtAutoreleasePool shows the following code:

    struct __AtAutoreleasePool {
      __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush(a); } ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj); }void * atautoreleasepoolobj;
    };
    Copy the code
    • By looking at the structure, you can see that it is called in the constructorobjc_autoreleasePoolPushIs called when it is destructedobjc_autoreleasePoolPop, coupled withOCBreakpoints and assembly, and finally determine the code inlibobjc.A.dylib, so inObjc4-818.2 -Source code analysis of the core code
  • Objc_autoreleasepoolpage (push) and objC_AutoreleasepoolPage (pop);

    void *
    objc_autoreleasePoolPush(void)
    {
        return AutoreleasePoolPage::push(a); }void
    objc_autoreleasePoolPop(void *ctxt)
    {
        AutoreleasePoolPage::pop(ctxt);
    }
    Copy the code

    Let’s look at AutoreleasePoolPage

AutoreleasePoolPage

    1. findAutoreleasePoolPageYou can see the comment at the beginning of the method:



    • In the comments, you can see that the auto-release pool holds Pointers to either objects or to the boundaries of the release poolPOOL_BOUNDARYThere’s another one in the poolThe sentry objectThe auto release pool is oneTwo-way linked list.
    1. AutoreleasePoolPageHas its own constructor and destructor:



AutoreleasePoolPageData

  • AutoreleasePoolPage inherits AutoreleasePoolPageData, which is a structure



    The parameters are described as follows:

    • magic: used for verificationAutoreleasePoolPageIs the structure complete
    • next: indicates the latest additionautoreleasedThe next position of the object, that’s what this object was initialized tobegin()
    • thread: points to the current thread
    • parent: refers to the parent node, the first nodeparentA value ofnil
    • child: refers to the child node, the last nodechildA value ofnil
    • depth: represents depth, from0It starts to increase backwards1. So each additional page increases1
    • hiwatRepresentative:high water mark, the maximum number of stacks
  • The AutoreloeasePool structure is analyzed in the code below

The code analysis

  • Create an empty project, change the environment to MRC, add objects to @Autoreleasepool, and print autoreleasePool:

    extern void _objc_autoreleasePoolPrint(void);
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            NSObject *obj = [[NSObject alloc] autorelease];
            _objc_autoreleasePoolPrint();
        }
        return 0;
    }
    Copy the code
      1. _objc_autoreleasePoolPrintIs in theobjcThe source is a function that prints the contents of the auto-release pool.
      1. @autoreleasepoolIs the automatic release pool,autoreleaseIs to add an object to the automatic release pool
  • The print result is as follows:



    • You can observe the automatic release pool in front of56 bytesInitializes a member variable associated with, and then has a8 bytestheThe sentry object, before objects are added to the auto-release pool.

Home Page Structure

  • Based on the above analysis, the structure of the first page of autoreleasePool is shown below



How does the auto-release pool add destruction objects, how many destruction objects can be added to a page, and when objects are released? We will examine these properties in principle

The principle of

push

  • Let’s start with the push code

    static inline void *push(a)
    {
        id *dest;
        if (slowpath(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;
    }
    Copy the code

    Without looking at the Debug code, you can locate the autoreleaseFast code

  • The autoreleaseFast code is as follows:

    static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage(a);/ / get hotpage
        if(page && ! page->full()) { // If the page exists and is not full, add the object
            return page->add(obj); 
        } else if (page) { // If the storage is full, it will be added
            return autoreleaseFullPage(obj, page); 
        } else { // If page does not exist, create page
            return autoreleaseNoPage(obj); }}Copy the code
    • fromhotPageGet to thepageThe latter is mainly divided into three situations:
        1. pageIf it exists and is not full, add
        1. pageIf it is full, it callsautoreleaseFullPageMethod added after paging
        1. pageCall if it does not existautoreleaseNoPagePerform related creation and add operations

    The following is a detailed analysis of the three cases

add

  • At the heart of the ADD code is a memory shift operation



    • nextThe pointer starts out pointingbeginPosition, when one comes inobj.nextIt’s going to shift it down by one until it’s full
    • nextThe pointer translation process is shown as follows:



The judgment is full

  • First let’s look at the condition full:



    • whennextPoint to theendThe position, which is 1, 2, 3pageAnd at the end of that, it’s full, you can see thatsizeThe maximum value of2 ^ 12That is4096, because the inside member variables account for56Bytes, the sentinel object accounted for8 bytes, so you can add the object account4032Bytes, that is, can be added504Destroy the object.
  • Code verification:



    • When the 505th is added it is paginated and printed as follows



      • The first505Three objects were added to the newpageIn the newpageDoes not have a member variable that handles initialization needsThe sentry object, that is, new page occupancy4040It’s a byte, which isCan be stored505An object
    • Question: why is there no sentinel object on page 2?

      • A: Because the sentry object has “monitoring”pageBoundary function, the first page is already full is not neededaddOperation, so it can go to the second page to work, because it will work on the current page until it is full, so only one sentinel object is enough

Conclusions: 1. The first page can store a maximum of 504 objects, and the new page can store a maximum of 505 objects. 2. The new page has no sentinel object

autoreleaseFullPage

  • When full, the autoreleaseFullPage method is used



    • Here is mainlydo-whileFind the last one in the looppage
      • If not, willpageSet tohotCall again,addaddobj
      • If it is full, it is calledAutoreleasePoolPageCreate a newpageAnd thenaddaddobj.
  • The AutoreleasePoolPage method initializes the page and sets the value of the member variable, and if there is a parent, sets the parent’s child as itself



autoreleaseNoPage

  • HotPage is created when no hotPage is retrieved, i.e. the first hotPage does not exist:



    • The creation process is the same as the full one above, but one will be added after the first interface is createdThe sentry object, and then add the destruction object

pop

  • The main code for POP is as follows



    • iftokenPoint to the sentinel object, indicating that the first page is empty, and do something about it
    • popPageDebugThe method will eventually go topopPageFunction, and look againpopPageThe implementation of the

popPage

The following code



    1. popPageIt first callsreleaseUntilApply the object torelease:



    • This code is mainly in theThe while loopTo fetch the object and then callobjc_releaseMethod to release
    • The process of translating objects is shown as follows:



    1. Object is released and the current page is emptyparentAnd then put the current pagekillAnd willparentSet tohotPage
    1. If it ispageIf it’s the first page, it’s directkillAnd willhotPageSet tonil
    1. If the currentpageThere arechild:
    • If the current page is half full, thechilddirectlykill
    • ifchildThere arechild, it willchildthechilddirectlykill

kill

  • The core of page destruction is the kill function, which is implemented as follows:



    kill, the process is relatively simple, is to get the current pagechildStart with the last onepagestartdelete, until the current page

The entire POP process is to first destroy the object release and then delete from the last one until the first one. The first page destroys release and sets hotPage to nil

Multi-page structure diagram



Related topics

    1. AutoreleasePoolCan it be nested?
    • The code tests are as follows
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            NSObject *obj1 = [[NSObject alloc] autorelease];
            NSLog(@"obj1 : %@", obj1);
          
            @autoreleasepool {
                NSObject *obj2 = [[NSObject alloc] autorelease];
                NSLog(@"obj2 : %@", obj2);
                _objc_autoreleasePoolPrint();
            }
            _objc_autoreleasePoolPrint();
        }
        return 0;
    }
    Copy the code
    • Print result:



    • As you can see from the result, there are two sentinels in the first print, that is, the contained autorelease pool is pushed into the outer autorelease pool, so there are two sentinels in the print, and when out of scope, the inner autorelease pool is freed, so there is only one sentinel and obj1 in the print below.

    • A: Auto release pools can be nested

    1. ARCHow are objects added to the auto-release pool?
    • We know thatMRCIn, the created object needs to be appendedautoreleaseBefore you can go into the poolARCThere is noautorelease, you can use__autoreleasingModifier to add an object to an automatic release pool:
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            NSObject *obj1 = [NSObject alloc] ;
            NSLog(@"obj1 : %@", obj1);
          
            NSObject * __autoreleasing obj2 = [NSObject alloc] ;
            NSLog(@"obj2 : %@", obj2);
            _objc_autoreleasePoolPrint();
        }
        return 0;
    }
    Copy the code

    The print result is as follows:



    • ARCThere are mainly the following situations when entering the pool:If it is notalloc/new/copy/mutableCopyAutomatically registers the returned object toAutoreleasepool