Chapter 1 automatic reference counting

1.1 What is automatic reference Counting

  • Concept: If you set ARC(Automaitc Reference Counting) to a valid state in the LLVM compiler, you do not need to type it againretainreleaseThe code.

1.2 Memory Management/Reference Counting

1.2.1 profile

  • Reference counting is like the lighting of an office lamp

    Action done to a lighting device Actions taken on OC objects
    Turn on the light To generate the object
    Need lighting Hold the object
    No lighting required Release object
    Turn off the lights Discarded objects
  • When A generates an object, the reference count is 1. When one more person needs lighting, for example, B needs lighting, the reference count is +1, and so on. When A no longer needs an object, A frees the object, and the reference count is -1. When the last person who owned the object no longer wants it, the reference count goes to 0 and the object is discarded.

1.2.2 Thinking mode of Memory management

  • Objective and correct way of thinking:

    • Self generated objects, self held
    • Objects that are not generated by themselves can be held by themselves
    • Release an object you hold when it is no longer needed
    • Objects that are not owned by you cannot be released
    The object operation OC method
    Generate and hold objects Alloc/new/copy/mutableCopy, etc
    Hold the object retain
    Release object release
    Discarded objects dealloc
  • Self generated objects, own: hold objects

    - (id) allocObject {

    // Generate and hold your own objects

      id obj = [[NSObject alloc] init];

      

      return obj;

    }

    Copy the code
  • Note that the array method of the NSMutableArray class does not hold an object. Its internal implementation principle is as follows:

    - (id)object {

    // Generate and hold your own objects

      id obj = [[NSObject alloc] init];

      

    // Register the object in autoreleasepool. When the pool ends, release is automatically called. This method does not hold the object itself.

      [obj autorelease];

      

    // Return the object that you don't own.

      return obj;

    }

    Copy the code
    • You can also hold objects that you didn’t generate yourself: although you didn’t initially hold them, you can use retain to make them your own, and then you can use release to release objects.

      // Get a non-generated object

        id obj = [NSMutableArray array];



      // The obtained object exists, but it is not owned. The reference count is still 0, but the object is placed in autoReleasepool and can be released automatically

        [obj retain];

      // At this point, you own the object with a reference count of 1



        [obj release];

      // The object is released, the reference count becomes 0, and the object can no longer be accessed, but the object is not immediately deprecated

      Copy the code
  • Unable to free objects that are not owned by you: for example

    // Get objects that are not owned by you

    id obj = [NSMutableArray array];



    [obj release];

    // Will cause the program to crash

    Copy the code

1.2.3 alloc/retain/release/dealloc implementation

  • Analyze the GNU source to understand the methods in the NSObject class.

    • Alloc id obj = [[NSObject alloc] init];

      + (id)alloc {
      // alloc internally calls allocWithZone
        return [self allocWithZone:NSDefaultMallocZone()];
      }
      
      + (id)allocWithZone:(NSZone *)zone {
      // allocWithZone internally calls NSAllocateObject
        return NSAllocateObject(self, 0, z);
      }
      
      struct obj_layout {
        NSUInteger retained;
      };
      
      inline id
      NSAllocateObject (Class aClass, NSUInteger extreBytes, NSZone *zone) {
        intSize = Calculate the size of memory needed to hold the object;// Allocate memory space
        id new = NSZoneMalloc(zone, size);
        // Initialize the value in this memory space to 0
        memset(new.0, size);
        // Returns the pointer used as an object
        new = (id)&((struct obj_layout *) new) [1];
      }
      
      /** Where, NSZoneMalloc, NSDefaultMallocZone() and other names contain zones that are introduced to prevent memory fragmentation. The area of memory allocation itself is multi-managed, according to the purpose of object use, size, allocation of memory, thus improving the efficiency of memory management. However, current knowledge of run-time systems simply ignores the concept of regions. Memory management in run-time systems is already efficient, and using regions to manage memory will lead to inefficient memory use. * /
      
      Copy the code
    • Simplified code after removing NSZone

      struct obj_layout {
        NSUInteger retained;
      };
      
      + (id)alloc {
        int size = sizeof(struct obj_layout) + object size;Struct obj_layout (struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout, struct obj_layout (struct obj_layout, struct obj_layout, struct obj_layout)
        struct obj_layout *p = (struct obj_layout *)calloc(1, size);
        // Returns a pointer to the object
        return (id)(p + 1);
      }
      Copy the code
    • [obj retain]; The implementation of the

      - (id)retain {
        NSIncrementExtraRefCount(self);
      }
      
      inline void
      NSIncrementExtraRefCount(id anObject) {
        Struct obj_layout * // Struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / struct obj_layout * / If not, retained++ makes the reference count +1.
        if (((struct obj_layout *) anObject)[- 1].retained == UINT_MAX - 1) {
          [NSException raise: NSInternalInconsistencyException format:@"NSIncrementExtraRefCount() asked to increment too far"];
          
          ((struct obj_layout *) anObject) [- 1].retained++; }}Copy the code
    • [obj release] implementation

      - (void)release {
        if(NSDecrementExtraRefCountWasZero(self)) { [self delloc]; }}BOOL
      NSDecrementExtraRefCountWasZero(id anObject) {
        if (((struct obj_layout *) anObject)[- 1].retained == 0) {
          return YES;
        } else {
          ((struct obj_layout *) anObject)[- 1].retained--;
          returnNO; }}Copy the code
    • [obj dealloc]; The implementation of the

      - (void)dealloc {
        NSDeallocateObject(self);
      }
      
      inLine void
      NSDeallocateObject (id anObject) {
      // The pointer o points to the memory address of anObject, and then releases the memory that this pointer points to
        struct obj_layout *o= ((struct obj_layout *) anObject) [1];
        free(o);
      }
      Copy the code

1.2.4 Realization of Apple

  • First look at the implementation of alloc:

    // Call the four methods in turn

    + alloc

    + allocWithZone:

    class_Instance

    calloc

    Copy the code
  • Implementation of retainCount/retain/release

    - retainCount

    __CFDoExtrernRefOperation

    CFBaseicHashGetCountOfKey

      

      

    - retain

    __CFDoExternRefOperation

    CFBasicHashAddValue;



    - release

    __CFDoExternRefOperation

    CFBasicHashRemoveValue

      

    // The prefix CF for these functions indicates that they are included in the source code of the Core Foundation framework

    Copy the code

    So the internal implementation might look like this:

    int __CFDoExternRefOperation(uintptr_r op, id obj) {

    CFBasicHashRef table = Get object hash table (obj);

      int count;

      

      switch (op) {

        case OPERATION_retainCount:

          count = CFBasicHashGetCountOfKey(table, obj);

          return count;

        case OPERATION_retain:

          CFBasicHashAddValue(table, obj);

          return obj;

        case OPERATION_release:

          count = CFBasicHashRmoveValue(table, obj);

    // If count == 0 and YES are returned, dealloc is called

          return 0 == count;

      }

    }



    // Give an example of retainCount



    - (NSUInteger)retainCount {

      return (NSUInteger)__CFDExternRefOperation(OPERATION_retainCount, self);

        }



    Copy the code
  • As you can see, Apple probably manages reference counts in a hash table inside the count. Review hash table

  • To compare

    • Benefits of managing reference counts through memory block headers:

      • It can be done with a little code

      • The ability to uniformly manage the memory blocks needed for reference counting and the memory blocks used by objects

    • The benefits of managing reference counts through reference count tables

      • The memory block used by the object is allocated regardless of its header (with one less header to count references).
      • Each record in the reference counter table contains the address of memory blocks, which can be traced from each record to each object’s memory block. This causes the timing to fail and the block of memory used by the object is corrupted. While reference counts are managed in the header of the block, we cannot access the block, but when reference counts are managed in the reference counter table, we can address the location of the block.
      • In addition, when using tools to detect memory leaks, reference count tables can also be used to detect whether individual objects have holders

1.2.5 autorelease

  • Autorelease treats object instances like C automatic variables. When it goes out of scope, the object is called release.

  • How to use autoRelease:

    • Generates and holds an NSAutoreleasePool object

    • Calls the autoRelease instance method of the allocated object

    • Deprecating the NSAutoreleasePool object (automatically calling release on the object)

      // The code is as follows

          NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init];

          id obj = [[NSObject alloc] init];

          [obj autorelease];

      [pool drain]; => equivalent to [obj release];

      Copy the code
  • We don’t need to explicitly call the Pool object in programming, because in RunLoop it takes care of that for us. In a RunLoop, the NSAutoreleasePool object is generated, processed by the main thread of the application, and discarded.

  • However, we sometimes need to explicitly call the NSAutoreleasePool object, because sometimes a large number of AutoRelease objects are generated, and as long as NSAutoreleasePool objects are not discarded, these generated objects cannot be released. This can cause excessive memory growth. The most typical example is to change the size of a large image while reading it.

    • The image file reads the NSData object and generates a UIImage object from it. Changing the size of this object generates a new UIIamge object. This will result in a large number of AutoRelease objects. It is then necessary to generate, hold, or discard the NSAutoreleasePool object in the appropriate place.
  • In addition, there are many class methods in the Cocoa framework that return autoRelease objects. Such as

    id array = [NSMutableArray arrayWithCapasity:1];



    / / equivalent to the

    id array = [[[NSMuatbleArray alloc] initWithCapasity:1] autorelease];

    Copy the code

1.2.6 Implementation of AutoRelease

  • Let’s start with the source code for GNU

  • First look at the implementation of the AutoRelease method

    [obj autorelease];



    // The ostensible implementation method

    - (id)autorelease {

      [NSAutoreleasePool addObject:self];

    }



    / * *

    In fact, the autoRelease is implemented internally using the Runtime's IMP Caching method. When a method call is made, to resolve the function pointer to the method run in the class/method fields, they are cached at framework initialization

    * /

    id autorelease_class = [NSAutoreleasePool class];

    SEL autorelease_sel = @selector(addObject:);

    IMP autorelease_imp = [autorelease_class methodForSelector:autorelease_sel];



    // The actual method call uses the cached result value

    - (id)autorelease {

      (*autorelease_imp)(autorelease_class, autorelease_sel, self);

    }

    Copy the code
  • Now look at the addObject class method implementation of NSAutoreleasePool

    + (void)addObject:(id)obj {

    NSAutoreleasePool *pool = Get the NSAutoreleasePool object in use;

      if (pool) {

        [pool addObject:anObj];

      } else {

    NSLog(" there is no NSAutoreleasePool object in use ");

      }

    }

    Copy the code
    • Note: When multiple NSAutoreleasePool objects are nested, the innermost NSAutoreleasePool object is naturally called
  • AddObject instance method implementation

    // This object is added to the NSAutoreleasePool object array when the NSObject class's AutoRelease instance method is called

    - (void)addObject:(id)obj {

      [array addObject:obj];

    }

    Copy the code
  • The drain instance method disuses an NSAutoreleasePool object in use

    Drain () -> dealtypool () -> emptyPool() -> [obj release] -> emptyPool release]

    - (void)drain {

      [self dealloc];

    }



    - (void)dealloc {

      [self emptyPool];

      [array release];

    }



    - (void)emptyPool {

      for (id obj in array) {

        [obj release];

      }

    }

    Copy the code

1.2.7 Implementation of Apple

  • The realization of the c + +

    class AutoreleasePoolPage {
      static inline void *push(a) {
        // Generate or hold NSAutoreleasePool objects
      }
      static inline id autorelease(id obj) {
        // The addObject method corresponding to the NSAutoreleasePool classAutoreleasePoolPage * AutoreleasePoolPage = Get the AutoreleasePoolPage instance in use; autoreleasePoolPage -> add(obj); }static inline void *pop(void *token) {
        // Discard the NSAutoreleasePool object
        releaseAll();
      }
      id *add(id obj) {
        // Add objects to the internal array of AutoreleasePoolPage
      }
      void releaseAll(a) {
        // Call the release method of the inner array object}};// Specific call
    void *objc_autoreleasePoolPush(void) {
    
        return AutoreleasePoolPage::push();
    
      }
    
      void *objc_autoreleasePoolPop(void *ctxt) {
        return AutoreleasePoolPage::push(ctxt);
      }
    
      id *objc_autorelease(void) {
    
        return AutoreleasePoolPage::autorelease(obj);
    
      }
    
    Copy the code
  • Observe the NSAutoreleasePool class methods and the AutoRelease method in action

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // == objc_autoreleasePoolPush()



    id obj = [[NSObject alloc] init];



    [obj autorelease];

    // == objc_autorelease(obj)



    [pool drain];

    // == objc_autoreleasePoolPop(pool);

    Copy the code
  • [NSAutoreleasePool showPools]]; Can be used to confirm the status of an object that has been autoreleased.

  • Question: What if the AutoRelease NSAutoreleasePool object?

    • A: It crashes. Because usually when you use the Foundation framework, whenever you call the autoRelease method of any object, you’re essentially calling the AutoRelease method of the NSObject class. But the autoRelease method is already overridden by the NSAutoreleasePool class. So there will be errors at run time.

1.3 the ARC rules

1.3.1 profile

  • In fact, the essence of reference counting memory management has not changed in ARC. Like the name automatic reference counting, all ARC does is automatically help us deal with reference counting.

1.3.2 Thinking mode of memory management

  • The way to think about reference-counted memory is to think about the changes caused by ARC
    • Self generated objects, self held
    • Objects that are not generated by themselves can be held by themselves
    • Release an object you hold when it is no longer needed
    • Objects that are not owned by you cannot be released
  • It’s essentially the same way to think about memory management, but it’s implemented a little differently

1.3.3 Ownership modifier

  • When ARC is in effect, id types and object types are different from other types in C and must have an ownership modifier attached to their types. There are four types

    • __strong modifier
    • __weak modifier
    • __unsafe_unretained modifier
    • __autoreleasing modifier
  • __strong modifier

    • The __strong modifier is the default ownership modifier for ID types and object types

      // In the ARC valid environment

      id obj = [[NSObject alloc] init] <==> id __strong obj = [[NSObject alloc] init];



      // ARC is invalid

      {

        id obj = [[NSObject alloc] init] 

        [obj release];

      }

      Copy the code
    • When a self-generated object modified by the __strong modifier exceeds its scope:

      {

      // Generate and hold your own objects

        id __strong obj = [[NSObject alloc] init];

        

      / * *

      Because the variable obj is a strong reference, it holds the object itself

      * /

      }

      // Because the variable is out of scope, the strong reference is invalid, so the object is released, because the object now has no other owner, the object is discarded.

      // Just follow the memory management principles

      Copy the code
    • When the owner of the object and the life cycle of the object are clear, acquire objects that are not generated and held by the object itself:

      {

      // Get the non-generated object first, but hold it because __strong decorates it

        id __strong obj = [NSMutableArray array];

      }

      / * *

      When the object is out of scope, the strong reference fails, so the object is released.

      However, because the objects generated by [NSMutableArray Array] are not owned by themselves, but are automatically added to autoreleasePool, they are automatically discarded after a RunLoop period.

      * /

      Copy the code
    • Variables with the __strong modifier can be assigned to each other

      Obj0 strongly refers to object A, obj1 strongly refers to object B, indicating that obj1 holds B and obj2 does not hold any objects

      id __strong obj0 = [[NSObject alloc] init]; / / object A

      id __strong obj1 = [[NSObject alloc] init]; / / object B

      id __strong obj2 = nil;



      Obj0 and obj1 strongly refer to the same object B. No one holds object A anymore, so object A is deprecated.

      obj0 = obj1;



      // Obj2 now refers to the object held by obj0, so object B is now held by three references.

      obj2 = obj0;



      // Now obj1 has no strong reference to object B, so it now holds strong references to object B as obj0, obj2

      obj1 = nil;



      // Similarly, only obj2 now holds object B

      obj0 = nil;



      // There is no reference to object B, object B is discarded

      obj2 = nil;

      Copy the code
    • The __strong modifier can also be used to modify OC class member variables. You can also use variables with the _strong modifier on method arguments

      @interface Test : NSObject

      {

          id __strong obj_;

      }

      - (void)setObject:(id __strong)obj;

      @end

      @implementation Test



      - (instancetype)init

      {

          self = [super init];

          return self;

      }



      - (void)setObject:(id)obj {

          obj_ = obj;

      }



      @end

        

      // Call the function

      {

              

      // First test holds a strong reference to the test object

              id __strong test = [[Test alloc] init];

              

      // Obj_ member of the Test object, which holds a strong reference to the NSObject object

              [test setObject:[[NSObject alloc] init]];

      }

          

      / * *

      At this point the test strong reference is out of scope and it is invalidated.

      So there's no strong reference to the Test object, and the Test object will be discarded

           

      When the Test object is discarded, the obj_ member of the Test object is also discarded.

      So it frees a strong reference to NSObject

           

      Because NSObject has no other owners, the NSObject object is also deprecated.

      * /

      Copy the code
    • Like the _weak and _autoreleasing modifiers, the _strong and _autoreleasing modifiers automatically point the reference to nil when initialized, even if they are not explicitly stated. The _strong modifier perfectly satisfies the way you think about reference counting

    • The ownership modifier for both id types and object types is __strong by default, so there is no need to explicitly specify that the modifier object is _strong

  • __weak modifier

    • The _weak modifier was created to solve the problem of circular references caused by the _strong modifier in memory management. The above case:

      @interface Test : NSObject

      {

          id __strong obj_;

      }

      - (void)setObject:(id __strong)obj;

      @end

      @implementation Test



      - (instancetype)init

      {

          self = [super init];

          return self;

      }



      - (void)setObject:(id)obj {

          obj_ = obj;

      }



      @end

        

      // Call the function and print the variable as follows:

      {

              

      Test1 holds a strong reference to Test, and test2 holds a strong reference to Test

      id __strong test1 = [[Test alloc] init]; / / object A

      id __strong test2 = [[Test alloc] init]; / / object B



      / * *

      The obj_ member of TestA holds object B that test2 points to, and obj_ of object B that test2 points to strongly references object A, thus creating A circular reference.

      * /

              [test1 setObject:test2];

            [test2 setObject:test1];

      }

      / * *

      When out of scope, Test1 releases its strong reference to object A

      Test2 releases its strong reference to object B

      However, obj_A in object A's strong reference to object B should be released. However, since object A is strongly referenced in object B, obj_A will not be released and will continue to strongly reference object B. Similarly, obj_B in object B will not be released, so it will continue to strongly reference object A. So no one outside is referring to object A and object B, but they are referring to each other, causing A memory leak! (A memory leak is an object that should be deprecated but persists beyond the scope of its lifetime variable.)

      * /



      / * *

      Print the variable as follows:

      Test1 objects strongly reference test2 objects, and test2 objects strongly reference Test1 objects, causing an endless loop.

      * /

      Copy the code

  • And here’s the situation:

    {

      id test = [[Test alloc] init];

      [test setObject:test];

    }

    / * *

    When the scope for a strong reference to test ends, it releases the reference to the test object.

    However, the Test object also retains a strong reference to the Test object inside, so the Test object is referenced and therefore cannot be reclaimed

    * /

    // Memory leaks also occur!

    Copy the code

  • In this case, an __weak modifier is very much needed to avoid circular references

    // Weak references, as opposed to strong references, cannot hold object instances.

    // Write this to warn: core exchange retained object to weak variable; object will be released after assignment

    // indicates that the NSObject object is destroyed as soon as it is created because no one is holding it

    id __weak obj = [[NSObject alloc] init];



    // The correct way to use weak references

    {

    // Generate and hold your own NSObject object

            id obj = [[NSObject alloc] init];

      

    // Because NSObject is already strongly referenced by OBj, it doesn't matter if obj1 uses a weak reference to it,

    // Does not make its reference count +1

            id __weak obj1 = obj;

    }

    / * *

    When the variable is out of scope, obj's strong reference to NSObject disappears,

    No one's holding NSObject at this point.

    The NSObject object is discarded

    * /

    Copy the code
  • Modify the circular reference example above as follows:

    @interface Test : NSObject

    {

        id __weak obj_;

    }

    - (void)setObject:(id __strong)obj;

    @end

    @implementation Test



    - (instancetype)init

    {

        self = [super init];

        return self;

    }



    - (void)setObject:(id)obj {

        obj_ = obj;

    }



    @end

      

    // Call the function and print the variable as follows:

    {

            

    Test1 holds a strong reference to Test, and test2 holds a strong reference to Test

    id __strong test1 = [[Test alloc] init]; / / object A

    id __strong test2 = [[Test alloc] init]; / / object B



    / * *

    The obj_ member variable in TestA weakly refers to object B pointed to by test2, and obj_ in object B pointed by test2 weakly refers to object A.

    * /

            [test1 setObject:test2];

          [test2 setObject:test1];

    }

    / * *

    When out of scope, Test1 releases its strong reference to object A

    Test2 releases its strong reference to object B

    At this point, since the obj_ variable in the object only has A weak reference to the object, no one is holding object A, and object B. They are released without creating A circular reference!

    * /

    Copy the code
  • Another advantage of the __weak modifier: when holding a weak reference to an object, if the object is deprecated, the weak reference is automatically invalidated and is set to nil (empty weak reference)

    id __weak obj1 = nil;

            {

    // Generate and hold your own objects

                id __strong obj0 = [[NSObject alloc] init];

                

    // obj1 now also points to NSObject

                obj1 = obj0;

                

    // Print obj1 with a value

                NSLog(@"A = %@", obj1);

            }

            

    / * *

    When the variable obj0 goes out of scope, it no longer holds the NSObject object,

    Since obj1 is a weak reference, it also does not hold an NSObject object

    The NSObject object is deprecated because no one is holding it

      

    When deprecated, the weak reference to the obj1 variable is invalidated, and obj1 is reassigned to nil

    * /

    NSLog(@"B = %@", obj1);



    / * *

    The result is printed as follows:

    2017-12-14 15:16:39.859875+0800 littleTest[10071:1377629] A = <NSObject: 0x10054DA70 >

      2017-12-14 15:16:39.860432+0800 littleTest[10071:1377629] B = (null)

    * /

    Copy the code

  • __unsafe_unretained modifier

    • The _unsafe_unretained modifier is an insecure modifier and was used in place of the _weak modifier prior to iOS4

      id __unsafe__unretained obj1 = nil;

              {

                  id __strong obj0 = [[NSObject alloc] init];

                  

                  obj1 = obj0;

                  

                  NSLog(@"A = %@", obj1);

              }

              

      NSLog(@"B = %@", obj1);



      / * *

      The source code does not execute correctly because the __unsafe_unretained modifier makes the variable neither strongly nor weakly reference the object.

      When obj0 is out of scope, NSObject has no reference and is freed

      At the same time, Obj1 sometimes mistakenly accesses objects, resulting in the following print

      2017-12-14 15:38:21.462724+0800 littleTest[10140:1399554] A = <NSObject: 0x10044EEA0 >

      2017-12-14 15:38:21.463007+0800 littleTest[10140:1399554] B = <NSObject: 0x10044EEA0 >



      Sometimes errors occur that crash the program directly.

      The essence of both of these situations is that obj1 is accessing objects that are already deprecated, causing dangling Pointers!

      * /

      Copy the code
    • So, it’s important to note that when accessing an object using the _unsafe_unretained modifier, you must ensure that the object is actually real.

  • __autoreleasing modifier

    • When ARC is in effect, we cannot use the following code because it is used in a non-ARC environment:

      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

      id obj = [[NSObject alloc] init];

      [obj autorelease];



      [pool drain];

      Copy the code
    • As an alternative, we use ARC when it is in effect

      @autoreleasepool {        

         id __autoreleasing obj = [[NSObject alloc] init];

      }

      Copy the code
    • Their corresponding relationship is shown as follows:

  • We can use the __autoreleasing modifier unexplicitly.

  • Case 1: when has not generated and they hold the object, although you can use the alloc/new/copy/mutableCopy method to achieve object, but the object has been registered in the autoreleasePool. This is the same as when the AutoRelease method is called if ARC is invalid. This is because the compiler will automatically check whether the method name begins with a alloc/new/copy/mutableCopy, if not, then automatically registered object into autoreleasePool.

        @autoreleasepool {

    // First get the non-generated object, which obj holds because of its strong reference

    / / because the object name of the method is not begin with the alloc/new/copy/mutableCopy, so he was registered in autoreleasePool automatically.

          {

             id __strong obj = [NSMutableArray array];

          }

    / * *

    When a variable goes out of scope, it loses a strong reference to the object. So it frees the object it's holding

    However, autoreleasePool still holds a reference to this object, so it is not immediately obsolete

    * /

        }



    / * *

    When autoreleasePool's scope also ends, no one holds the object, so it is deprecated.

    * /

    Copy the code
    • Create object [NSMutableArray array]

      // Define an obj1 holding weak reference outside of autoReleasepool's scope

      id __weak obj1 = nil;

          

      @autoreleasepool {

              

              {

                  id __strong obj = [NSMutableArray array];

                  obj1 = obj;

                  NSLog(@"%@", obj1);

              }

              NSLog(@"%@", obj1);

      }

      NSLog(@"%@", obj1);



      / * *

      Print result:

      The 2017-12-14 16:32:07. 118513 + 0800 littleTest (10242-1444479),

      )

      The 2017-12-14 16:32:07. 118819 + 0800 littleTest (10242-1444479),

      )

      The 2017-12-14 16:32:07. 118850 + 0800 littleTest [10242-1444479] (null)



      It turns out that when obj0 goes out of its scope, it loses reference to the object. But because the object is automatically registered with autoReleasepool, obj1 still weakly references the object when the second NSLog prints. When the third NSLog prints, the object is destroyed because the AutoReleasepool has been emptied. Obj1 is reset to nil again

      * /

      Copy the code
  • [[NSMutableArray alloc] init]

    id __weak obj1 = nil;

          

          @autoreleasepool {

              

              {

                  id __strong obj = [[NSMutableArray alloc] init];

                  obj1 = obj;

                  NSLog(@"%@", obj1);

              }

              NSLog(@"%@", obj1);

          }

          NSLog(@"%@", obj1);



    / * *

    The print result is as follows:

    The 2017-12-14 16:36:09. 584864 + 0800 littleTest (10257-1449554),

        )

    The 2017-12-14 16:36:09. 585131 + 0800 littleTest [10257-1449554] (null)

    The 2017-12-14 16:36:09. 585149 + 0800 littleTest [10257-1449554] (null)

        

    This is because, using alloc/new/copy/mutableCopy method to create objects, not to the object automatically into autoreleasePool, this makes when obj0 beyond its scope, strong reference to the NSMutableArray object for no one, The object is discarded.

    * /

    Copy the code
  • The following methods are called to get objects that are not generated and held by ourselves:

    + (id)array {

      return [[NSMutableArray alloc] init];

    }



    / * *

    This code also does not use the __autorelease modifier, so objects inside this method are not registered with autoreleasePool.

    * /



    // The above method can also be written as follows:

    + (id)array {

      id obj = [[NSMutableArray alloc] init];

      return obj;

    }

    / * *

    Because return makes obj go out of scope, the object it points to, NSMutableArray, is automatically released, but because return returns this object to the calling function as the return value of the function, it is not discarded. And because of this object generation method is to use it as a return value, not by the alloc/new/copy/mutableCopy method to create, so NSMutableArray object will automatically be added to the autoreleasePool

    * /

    Copy the code
    • Case two: When you access a variable with an __weak modifier, you are actually bound to access an object registered in autoreleasePool

      id __weak obj1 = obj0;

      NSLog(@"class=%@", [obj1 class]);



      / / equivalent to the

      id __weak obj1 = obj0;

      id _autoreleasing temp = obj1;

      NSLog(@"class=%@", [temp class]);



      / * *

      The reason for this is that the __weak modifier holds only weak references to the object, and there is no guarantee that the object will not be discarded when it accesses it. So we put the object he wants to access into autoreleasePool, so that the object is guaranteed to exist until the @Autoreleasepool block ends.

      * /

      Copy the code
    • Case 3: Since id obj <==> id __strong obj we would like to derive id *obj <==> id __strong *obj but this is not the case, Id * releasing obj <==> id __autoreleasing obj NSObject **obj <==> NSObject * __autoreleasing *obj, such that Pointers to id or objects that are not explicitly specified are appended with the __autoreleasing modifier

      // For example, this method in NSString

      stringWithContentsOfFile:(nonnull NSString *) encoding:(NSStringEncoding) error:(NSError * _Nullable __autoreleasing * _Nullable)



      // Use this method of the source code is as follows:

      NSError *error = nil;

      BOOL result = [obj performOperationWithError:&error];



      // The function declaration is as follows

      - (BOOL)performOperationWithError:(NSError **)error;

      / / equivalent to the

      - (BOOL)performOperationWithError:(NSError * __autoreleasing *)error;



      / * *

      The __autoreleasing modifier is used because we declare a pointer to error, the address from which we pass error. What happens inside this method is that it changes what the error pointer points to. Makes it point to an object generated by alloc. And we need to understand that the memory management way of thinking is: in addition to the alloc/new/copy/mutableCopy generated objects, other ways to generate objects are required to be registered in the autoreleasePool. And acquire objects that they do not own. So declaring the variable as (NSError * __autoreleasing *)error will do the trick.

      * /



      - (BOOL)performOperationWithError:(NSError * __autoreleasing *)error {

        *error = [[NSError alloc] initWithDomain:MyAppDomain code:errorCode userInfo:nil];

          return NO;

      }



      / * *

      Possible for not familiar with C language friend, here is not very understand why must the function parameter declarations for "pointer to pointer", this is because when we just put the parameter declarations of Pointers, method becomes as follows, when we pass a pointer to function, the default will be generated with the same instance variable pointer type. When we manipulate Pointers in this method, we think we're manipulating Pointers, but we're actually just copying instance variables. That is, in this case error is the instance variable of the copy. When this method ends, the error is released, along with the content to which it refers. So this external error is still pointing to nil. Nothing has changed.

      When NSError * __autoreleasing *)error is used, the instance variable is still copied, but the pointer to the pointer is copied, that is, it points to the same address as the parameter. Use *error inside the function to get the address of the pointer pointing to an NSError object. Thus, although the function's copied instance variable is destroyed when it goes out of scope, it changes the object to which the function's external error pointer points from nil to an NSError object.

      * /



      - (BOOL)performOperationWithError:(NSError *)error {

        error = [[NSError alloc] initWithDomain:MyAppDomain code:errorCode userInfo:nil];

          return NO;

      }

      Copy the code
  • For functions that pass Pointers and Pointers, see here

  • The 👇 code generates a compilation error:

    NSError *error = nil;

    NSError **perror = &error;

    //Pointer to non-const type 'NSError *' with no explicit ownership

    Copy the code
  • because

    // The ownership modifier of perror needs to be changed

    NSError *error = nil;

    NSError *__strong *perror = &error;



    The same is true for other types of ownership modifiers

    NSError __weak *error = nil;

    NSError *__weak *perror = &error;



    / * *

    But we were just calling

      - (BOOL)performOperationWithError:(NSError * __autoreleasing *)error

    Method does not convert NSError *error to the __autoreleasing modifier. Why?

    * /

    // In fact, the compiler automatically converts the modifiers for us

    NSError *error = nil;

    NSError *_autoreleasing *temp = nil;

    BOOL result = [obj performOperationWithError:&temp];

    error = temp;

    Copy the code
    • Like NSAutoreleasePool, AutoReleasepool can also be nested. In iOS applications, for example, the entire program is contained in the @Autoreleasepool block.

    • Implementations such as NSRunLoop are able torelease objects registered in AutoReleasepool at any time, whether ARC is valid or not.

1.3.4 rules

  • When compiling code while ARC is in effect, the following rules must be followed:

    • Cannot use retain/release/retainCount/autorelease
    • Cannot use the NSAllocateObject/NSDeallocateObject
    • The memory management method naming rules must be followed
    • Cannot display calls to dealloc
    • Use the @Autoreleasepool block instead of NSAutoreleasePool
    • An NSZone cannot be used
    • Object variables cannot be members of C language constructs
    • The conversion ID and void * are displayed
  • Cannot use retain/release/retainCount/autorelease

    • From Apple’s official note “When setting ARC to work, you do not need to type release or retain code again” or you will compile an error.
  • Cannot use the NSAllocateObject/NSDeallocateObject

    • We already know that when we call the alloc method of the NSObject class, we generate and hold the OC object, as shown in GNUstep. In fact, alloc directly calls the NSAllocateObject function to generate the holding object, but in the ARC environment, If we also show that calling NSAllocateObject will generate a compilation error.
  • The memory management method naming rules must be followed

    • When the ARC is invalid, used for object generated/hold you need to follow these rules: alloc/new/copy/mutableCopy. A method that starts with the above name must return an object that the caller should hold. This is also true when ARC is in effect.

    • When ARC is in effect, a naming rule is appended: init

      • Begin with the init method rules than alloc/new/copy/mutableCopy more strictly. The method must be an instance method. Class methods are not allowed. And the returned object should be of type ID or the object type of the class declared by the method, or a superclass or subclass of that class. This return object is not registered with Autoreleasepool. Basically just initialize the object that the alloc method returns and return that object.

        // The following is the source code that uses this method: init initializes the value returned by the alloc method and returns the object

        id obj = [[NSObject alloc] init];



        // The following is not allowed because it does not return an object

        - (void)initTheData:(id)data;



        // In addition, the 👇 method also has init, but it is not included in the naming convention because it is a word initialize

        - (void)initialize;

        Copy the code
  • Cannot display calls to dealloc

    • Because the object’s dealloc method is called anyway when the object is obsolete, we don’t need to call it manually. When we call it manually, we get a compilation error
    • The dealloc method is used in most cases to delete a registered agent or observer object
    • If ARC is invalid, the dealloc method of its parent class must be explicitly called inside the dealloc method[super dealloc];
    • This is all handled automatically when ARC is in effect.
  • Use the @Autoreleasepool block instead of NSAutoreleasePool

    • Using NSAutoreleasePool in ARC causes a compilation error
  • An NSZone cannot be used

    • Whether or not ARC is valid, NSZone has been completely ignored by the current runtime system.
  • Object variables cannot be members of C language constructs

    • C constructs with OC object type variables cause compilation errors

    • Retained objects can be converted to void * or retained with the _unsafe_unretained modifier. The retained object is no longer part of the compiler’s memory management.

      struct Data {

        NSMutableArray __unsafe__unretained *array

      }

      Copy the code
  • The conversion ID and void * are displayed

    • Forcing the ID variable to void * is not a problem when ARC is invalid

      id obj = [[NSObject alloc] init];



      void *p = obj;



      // Assign void * to the id variable and call its instance method, and there will be no problems at runtime

      id o = p;

      [o release];

      Copy the code
    • When ARC is in effect, a compilation error is caused. In this case, assignment of id or object variables to void * or reverse assignment requires a specific conversion. For pure assignment, bridge conversion can be used

      id obj = [[NSObject alloc] init];



      // Return id to void *, which is less secure than __unsafe__unretained

      void *p = (__bridge void *)obj;



      // void * converts to id

      id o = (__bridge id)p;

      Copy the code
    • The __bridge translation also includes _bridge_retained and _bridge_transfer

      id obj = [[NSObject alloc] init];

      void *p = (__bridge_retained void *)obj

      Copy the code
      • This code is in a non-ARC environment

        id obj = [[NSObject alloc] init];



        void *p = obj;

        // __bridge__retained to retain, so that both P and obj hold this object

        [(id)p retain];

        Copy the code
    • Another example:

      void *p = 0;

      {

        id obj = [[NSObject alloc] init];

        

        p = (__bridge_retained void *)obj;

      }

      NSLog(@"class = %@", [(__bridge_retained)p class]);

      Copy the code
      • This code is in a non-ARC environment

        void *p = 0;

        {

          id obj = [[NSObject alloc] init];

          

          p = [obj retain];

          

          [obj release];

        }



        / * *

        P still holds a reference to the NSObject object

        * /

        NSLog(@"class = %@", [(__bridge_retained)p class]);

        Copy the code
    • __bridge_transfer, the object held by the converted variable is released after the variable is assigned to the converted target variable

      id obj = (__bridge_transfer id)p;

      Copy the code
      • Non-arc environment

        id obj = (id)p;

        [obj retain];

        [(id)p release];

        Copy the code
    • Objective-c objects versus Foundation objects

      • Core Foundation objects are used primarily in the Core Foundation framework, which is written in C and counts objects by reference. Retain and Release in the Core Foundation framework are CFRetain and CFRelease, respectively, when ARC is invalid
      • Core Foundation objects differ from OC objects only if they are generated by Core Foundation framework or Foundation framework. Objects generated by any framework, once generated, can be used by other frameworks. For example, objects generated and held by the Foundation API can be freed by the Core Foundation API.
      • Core Foundation objects are no different from Objective-C objects, so simple C transformations can be interchangeable when ARC does not work. In addition, this interchange requires no CPU resources, so it is also called a toll-free Bridge.

1.3.5 properties

  • The relationship between the property of the property declaration and the ownership modifier

    Property Specifies the property of the declaration Ownership modifier
    assign _unsafe_unretained
    copy __strong(assigns the object to be copied)
    retain __strong
    strong __strong
    unsafe_unretained _unsafe_unretained
    weak __weak

1.3.6 array

  • Static array case:

    // The use of variables with various modifiers as static arrays

    / / such as

    id __weak obj[10];

    // The __unsafe__unretained modifier always defaults the element value to nil

    Memory management also applies to the objects in an array when the array is out of its variable scope

    Copy the code
  • Dynamic array: In this case, containers from the Foundation framework such as NSMutableArray, NSMutableDictionary, and NSMutableSet will be used for different purposes, and these containers will hold the appending objects appropriately and manage them for us.

  • Take a look at the implementation of dynamic arrays in C

    // First, declare a dynamic array using Pointers. To represent the address of the pointer

    id __strong *array = nil;

    Since the default modifier for Pointers of type ID * is id __autoreleasing *, it is necessary to specify the __strong modifier for display. In addition, while id variables with the __strong modifier are guaranteed to be initialized to nil, array variables, which are id pointer variables, are not guaranteed to be initialized to nil

    // If the type is another type, the following is true:

    NSObject * __strong *array = nil;



    // After that, use the calloc function to ensure that you want to allocate the memory block occupied by the capacity of the __strong modifier variable

    array = (id __strong *)calloc(entries, sizeof(id));

    "// Where entries indicate the number of memory blocks. And the calloc function automatically initializes every object to which a variable in the array points to nil



    // If you use the malloc function to allocate memory, you need to manually initialize each variable to 0. Note that only memset and other functions can be used for initialization assignment



    // The dynamic array allocated by calloc can then be used exactly as a static array

    array[0] = [[NSObject alloc] init];



    // It is very different from a static array to operate on a variable with the __strong modifier. You need to free the array manually, but when it is freed, you must manually set every variable in the array to nil. You cannot use memset to set the value of the elements in the array to 0. This can also leak memory

    for (NSInteger i = 0; i < entries; ++i) {

      array[i] = nil;

    }

    free(array);

    Copy the code

1.4 Implementation of ARC

1.4.1 __strong modifier

  • __strong modifiers attached to assigned to variables in practical applications is how to run, 👇 code (the first is the normal can make reference count + 1 alloc/new/copy/mutableCopy method) :

    {

      id __strong obj = [[NSObject alloc] init];

    }

    Copy the code
    • When this code is converted to assembly code, it becomes (see my other article on how to convert to assembly code) :

      .section        __TEXT,__text,regular,pure_instructions

              .macosx_version_min 10, 13

              .globl  _main

              .p2align        4, 0x90

      _main:                                  ## @main

              .cfi_startproc

      ## BB#0:

              pushq   %rbp

      Lcfi0:

              .cfi_def_cfa_offset 16

      Lcfi1:

              .cfi_offset %rbp, -16

              movq    %rsp, %rbp

      Lcfi2:

              .cfi_def_cfa_register %rbp

              subq    $16, %rsp

              movl    $0, -4(%rbp)

              movq    L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rax

              movq    L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi

              movq    %rax, %rdi

              callq   _objc_msgSend

              leaq    -16(%rbp), %rdi

              xorl    %ecx, %ecx

              movl    %ecx, %esi

              movq    %rax, -16(%rbp)

              callq   _objc_storeStrong

              xorl    %eax, %eax

              addq    $16, %rsp

              popq    %rbp

              retq

              .cfi_endproc



              .section        __DATA,__objc_classrefs,regular,no_dead_strip

              .p2align        3               ## @"OBJC_CLASSLIST_REFERENCES_$_"

      L_OBJC_CLASSLIST_REFERENCES_$_:

              .quad   _OBJC_CLASS_$_NSObject



              .section        __TEXT,__objc_methname,cstring_literals

      L_OBJC_METH_VAR_NAME_:                  ## @OBJC_METH_VAR_NAME_

              .asciz  "alloc"



              .section        __DATA,__objc_selrefs,literal_pointers,no_dead_strip

              .p2align        3               ## @OBJC_SELECTOR_REFERENCES_

      L_OBJC_SELECTOR_REFERENCES_:

              .quad   L_OBJC_METH_VAR_NAME_



              .section        __TEXT,__objc_methname,cstring_literals

      L_OBJC_METH_VAR_NAME_.1:                ## @OBJC_METH_VAR_NAME_.1

              .asciz  "init"



              .section        __DATA,__objc_selrefs,literal_pointers,no_dead_strip

              .p2align        3               ## @OBJC_SELECTOR_REFERENCES_.2

      L_OBJC_SELECTOR_REFERENCES_.2:

              .quad   L_OBJC_METH_VAR_NAME_.1



              .section        __DATA,__objc_imageinfo,regular,no_dead_strip

      L_OBJC_IMAGE_INFO:

              .long   0

              .long   64

      .subsections_via_symbols        

      Copy the code
    • The simplified simulation code is:

      // First send a message to the NSObject class with the alloc instruction and assign the result to obj

      id obj = objc_msgSend(NSObject, @selector(alloc));

      // Init message is then sent to obj

      objc_msgSend(obj, @selector(init));

      // Finally release obj

      objc_release(obj);



      // The release method is automatically inserted while ARC is in effect

      Copy the code
  • Get an object not generated but held by yourself:

    id __strong obj = [NSMutableArray array];

    Copy the code
    • Convert to assembly language:

      .section        __TEXT,__text,regular,pure_instructions

              .macosx_version_min 10, 13

              .globl  _main

              .p2align        4, 0x90

      _main:                                  ## @main

              .cfi_startproc

      ## BB#0:

              pushq   %rbp

      Lcfi0:

              .cfi_def_cfa_offset 16

      Lcfi1:

              .cfi_offset %rbp, -16

              movq    %rsp, %rbp

      Lcfi2:

              .cfi_def_cfa_register %rbp

              subq    $16, %rsp

              movl    $0, -4(%rbp)

              movq    L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rax

              movq    L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi

              movq    %rax, %rdi

              callq   _objc_msgSend

              movq    %rax, %rdi

              callq   _objc_retainAutoreleasedReturnValue

              leaq    -16(%rbp), %rdi

              xorl    %ecx, %ecx

              movl    %ecx, %esi

              movq    %rax, -16(%rbp)

              callq   _objc_storeStrong

              xorl    %eax, %eax

              addq    $16, %rsp

              popq    %rbp

              retq

              .cfi_endproc



              .section        __DATA,__objc_classrefs,regular,no_dead_strip

              .p2align        3               ## @"OBJC_CLASSLIST_REFERENCES_$_"

      L_OBJC_CLASSLIST_REFERENCES_$_:

              .quad   _OBJC_CLASS_$_NSMutableArray



              .section        __TEXT,__objc_methname,cstring_literals

      L_OBJC_METH_VAR_NAME_:                  ## @OBJC_METH_VAR_NAME_

              .asciz  "array"



              .section        __DATA,__objc_selrefs,literal_pointers,no_dead_strip

              .p2align        3               ## @OBJC_SELECTOR_REFERENCES_

      L_OBJC_SELECTOR_REFERENCES_:

              .quad   L_OBJC_METH_VAR_NAME_



              .section        __DATA,__objc_imageinfo,regular,no_dead_strip

      L_OBJC_IMAGE_INFO:

              .long   0

              .long   64

              

      .subsections_via_symbols

      Copy the code
    • Simplified assembly language:

      // First send the array message to the receiver NSMutableArray, and then assign the return value of the result to obj

      id obj = objc_msgSend(NSMutableArray, @selector(array));



      / * *

      Obj_retainAutoreleasedReturnValue function is mainly used in the optimization program is running, just as its name implies obj_retainAutoreleasedReturnValue said is "holding Autorelease return values," said, It is a function that holds its own object, but the object it holds should return the method registered with the object in AutoReleasepool, or the return value of the function. Like this source, namely obj need to be modified in the call by __strong alloc/new/copy/mutableCopy method, other than by the compiler inserts the function

      * /

      objc_retainAutoreleasedReturnValue(obj);

      / / release obj

      objc_release(obj);

      Copy the code
    • Because, objc_retainAutoreleasedReturnValue function always come in pairs, so it actually has a sister: Objc_autoreleaseReturnValue, it mainly use the alloc/new/copy/mutableCopy method to generate object other than when the return of objects, as shown in 👇

      + (id)array {

        return [[NSMutableArray alloc] init];

      }



      // Convert to simplified code after assembly

      + (id)array {

        id obj = objc_msgSend(NSMutableArray, @selector(alloc));

        objc_msgSend(obj, @selector(init));

      // At this point, the method to register the object in Autoreleasepool is returned: The obj_autoreleaseReturnValue function is used to return objects registered in autoreleasepool, but the obj_autoreleaseReturnValue method is different from the obj_AutoRelease method, It is generally not limited to registering objects into Autoreleasepool

        return objc_autoreleaseReturnValue(obj);

      }



      / * *

      The objc_autoreleaseReturnValue method checks the list of methods or callers that use the function,

      1. If a method or function of the caller after calling a method or function then calls the objc_retainAutoreleasedReturnValue () function, then it will not return the object to register autoreleasepool, It is passed directly to the caller of a method or function.

      2. If a method or function of the caller in the call a method or function and then not call objc_retainAutoreleasedReturnValue () function, then it will return objects into autoreleasepool registration.



      And objc_retainAutoreleasedReturnValue () function and objc_retain function is different, he doesn't even registered to autoreleasepool, can correct access object.



      Through objc_autoreleaseReturnValue and collaboration of objc_retainAutoreleasedReturnValue method, we can not transfer object registered to autoreleasepool 2 directly, to optimize the process

      * /

      Copy the code

1.4.2 __weak modifier

  • As we saw earlier: the __weak modifier provides magic
    • If the object referenced by a variable with an __weak modifier is deprecated, nil is assigned to the variable
    • Using a variable with an __weak modifier is using an object registered in Autoreleasepool
If the object referenced by a variable with an __weak modifier is deprecated, then nil is assigned to the variable.
  • Let’s look at the implementation of the __weak modifier principle:

    {

      id __weak obj1 = obj;

    }



    / * *

    Compiler simulation code

    * /

    id obj1;



    // First initialize the variable with the __weak modifier through the obj_initWeak function

    objc_initWeak(&obj1, obj);



    // Then, at the end of the variable scope, release the variable using the obj_destroyWeak function

    objc_destroyWeak(&obj1);



    / * *



    After initializing a variable with an __weak modifier to 0, the objc_storeWeak function is called with the assigned object as an argument



    The obj_destroyWeak function calls the obj_storeWeak function with 0 as an argument



    * /



      objc_initWeak(&obj1, obj); <==> obj1 = 0; objc_storeWeak(&obj1, obj);



      objc_destroyWeak(&obj1) <==> objc_storeWeak(&obj1, 0);



    / * *



    The objc_storeWeak function takes the address of the assignment object of the second argument as the "key value" and registers the "address" of the first argument variable with the __weak modifier into the weak table. If the second argument is 0, the address of the variable is removed from the weak table



    The weak table is the same as the reference count table and is implemented as a hash table. If the weak table is used, the address of the abandoned object can be searched as the key value, and the address of the corresponding variable with the weak modifier can be obtained at a high speed. Also, because an object can be assigned to multiple variables with the weak modifier, it is possible to register the addresses of multiple variables for a single key value.



    * /



    Copy the code
  • How does the program operate when it discards objects that no one is holding? Let’s keep track of that. Objects will be released using objc_release

    • obj_release
    • The reference count is 0, so dealloc is executed
    • _objc_RootDealloc
    • object_dispose
    • objc_destrctInstance
    • objc_clear_deallocating
      • The action of objc_clear_deallocating is as follows
        • Gets the address of the deprecated object from the weak table as a record of key values
        • Assign nil to all addresses with an __weak modifier variable included in the record
        • Delete the record from the weak table
        • Removes the address of the deprecated object as a record of key values from the reference count table
  • The object referenced by the __weak modifier is deprecated, and the variable is set to nil to achieve this. However, you can see that if variables are heavily decorated with the _weak modifier, it can cause performance problems.

  • When using the __weak modifier, a warning is raised if the following is used

    id __weak obj = [[NSObject alloc] init];

    // The object will be released as soon as it is created



    // Compiler simulation code

      id obj;

      id tmp = objc_msgSend(NSObject, @selector(alloc));

      objc_msgSend(tmp, @selector(init));

    // Although the self-generated and held object is assigned to a variable with an __weak modifier through the objc_initWeak function, the compiler determines that it has no owner, so the object is immediately released through the objc_release method

      objc_initWeak(&obj, tmp);

      objc_release(tmp);

      objc_destroyWeak(&obj);

    // In this case, nil is assigned to variables with an __weak modifier that refer to discarded objects

    Copy the code
  • Some thoughts on releasing objects immediately

    // The following code is known to raise a warning from the compiler, because the compiler determines that the generated and held object can no longer be held because there is no strong reference to it

    id __weak obj = [[NSObject alloc] init];



    // ---------------------------------------------------------------------------------

    // What happens with the __unsafe_unretained modifier? Warnings are also generated

    id __unsafe_unretained obj = [[NSObject alloc] init];



    // Convert to compiler simulation code:

    id obj = obj_msgSend(NSObject, @selector(alloc));

    objc_msgSend(obj, @selector(init));

    // the obj_release function immediately releases the generated and held object, so that its dangling pointer is assigned to obj

    objc_release(obj);



    // ---------------------------------------------------------------------------------



    // What if the object is not assigned to a variable when it is generated?



    // In a non-ARC environment, memory leaks are bound to occur



    // But in ARC, obj_release is immediately called because it can no longer hold the object. This code does not leak memory due to ARC handling



      [[NSObject alloc] init];



    // ARC generated code



      id tmp = obj_msgSend(NSObject, @selector(alloc));



      objc_msgSend(tmp, @selector(init));



      objc_release(tmp);



    // ---------------------------------------------------------------------------------



    // Can I call the instance method of the object that is immediately freed?



      (void)[[[NSObject alloc] init] hash];



    // The code will look like this:



      id tmp = obj_msgSend(NSObject, @selector(alloc));



      objc_msgSend(tmp, @selector(init));



      objc_msgSend(tmp, @selector(hash));



      objc_release(tmp);



    // Therefore, the obj_release method is not called until the instance method of the object is called, so the instance method of the immediately freed object can be called



    Copy the code
Using a variable with an __weak modifier is an example of using an object registered with Autoreleasepool.
  • See 👇 code

    {

      id __weak obj1 = obj;

      NSLog(@"%@", obj1);

    }



    // The source code can be converted to the following form

    id obj1;

    objc_initWeak(&obj1, obj);

    // objc_loadWeakRetained retained the object referenced with the __weak modifier variable and retained

    id tmp = objc_loadWeakRetained(&obj1);

    // Register the object in autoreleasepool

    objc_autorelease(tmp);

    NSLog(@"%@", tmp);

    objc_destroyWeak(&obj1);

    Copy the code
  • Since the object referenced by the variable with the __weak modifier is registered with autoreleasepool like this, it is safe to use the _weak modifier variable until the end of the @Autoreleasepool block. However, you should not use a variable with the _weak modifier too much, as this will increase the number of objects registered in AutoReleasepool. Therefore, when using a variable with the _strong modifier, it is best to temporarily assign it to a variable with the _strong modifier before using it. If you don’t understand it, you can just look at the following code:

    // The following code registers the object assigned to the variable o into autoreleasepool five times

    {

        id __weak o = obj;

        for (int i = 0; i < 5; ++i) {

            NSLog(@"%d -- %@", i, o);

        }   

    }



    // The following code registers the object assigned to o only once in Autoreleasepool



      {



        id __weak o = obj;

        id tmp = o;

            for (int i = 0; i < 5; ++i) {

              NSLog(@"%d -- %@", i, tmp);

          }   

        



      }



    Copy the code
The __weak modifier is not supported
  • The __weak modifier is not supported in iOS4 and OS X Snow Leopard.
  • Classes that do not support __weak modifiers: NSMachPort, etc., which overrides the retain/release method and implements its own reference counting.
  • Classes that do not support the __weak modifier declare an attribute — ((objc_arc_WEAK_Reference_UNAVAILABLE)) attached to their classes, NS_AUTOMATED_REFCOUNT_WEAK_UNAVAALIBLE is also defined
  • There is another case in which the __weak modifier cannot be used, and that is whenallocWeakReference/retainWeakReferenceCase where the instance method returns NO. (This is not written into the NSObject class’s interface documentation), that is, these two methods are not normally covered.

1.4.3 __autoreleasing modifier

  • Assigning an object to a variable with the __autoreleasing modifier is equivalent to calling the object’s autorelease method when ARC is invalid.

  • First look at the use alloc/new/copy/mutableCopy

      @autoreleasepool {

        id __autoreleasing obj = [[NSObject alloc] init];

      }



    // simulate code

      id pool = objc_autoreleasePoolPush();

      id obj = objc_msgSend(NSObject, @selector(alloc));

      objc_msgSend(obj, @selector(init));

      objc_autorelease(obj);

      objc_autoreleasPoolPop(pool);

    Copy the code
  • Look at the outside using alloc/new/copy/mutableCopy method

    @autoreleasepool {

      id __autoreleasing obj = [NSMutableArray array];

    }



    // simulate code

    id pool = objc_autoreleasePoolPush();

    id obj = objc_msgSend(NSMutableArray, @selector(array));

    objc_retainAutoreleasedReturnValue(obj);

    objc_autorelease(obj);

    objc_autoreleasPoolPop(pool);



    / / although hold object obj methods into objc_retainAutoreleasedReturnValue, but the object obj reference to register the autoreleasepool method does not change

    Copy the code

1.4.4 Reference Counting

  • What exactly is the reference count value itself?

    // This function is the value of the function to get the reference count

    uintptr_t _objc_rootRetainCount(id obj);



    {

        id __strong obj = [[NSObject alloc] init];

      NSLog(@"retain count = %d", _objc_rootRetainCount);

    }

    // Print the result: retain count = 1

    Copy the code
  • We can’t actually trust the _objc_rootRetainCount function with all its values, because it sometimes returns 1 for freed objects and incorrect object addresses. And in multithreading, the reference count value of an object is used, because there are race states, so the value obtained is not always completely reliable

1.5 summarize

  • So far, we have explored the automatic reference counting has been completely explained, if there are omissions or incorrect, inaccurate place, we also look forward to criticism and correction, common progress.