1. What is automatic reference counting?

Memory management is achieved by automatic reference counting techniques.

  1. Memory management under MRC
  • Overview: System management of an object is usually a reference counting scheme. Here’s an example: Poor condition of senior high school study room only one lamp, the first way of self-study people need to turn on the lights at night (in the open or create objects), and after each and every one in people all need lighting (lighting that hold objects), each one to leave people no longer need the lighting (no longer need to lighting the release object), The last person to leave needs to turn off the lights. Thus, it is possible to manage whether the lights are on or off by counting the number of people who need them.

  • Memory management principles:

    1. Self-generated objects are held by themselves.
    2. Objects that are not generated by themselves can be held by themselves.
    3. Release when you don’t need to use your own objects.
    4. Objects that are not owned by you cannot be released.
    • Alloc, new, copy, mutableCopy, or method names that start with them are all held by the generated object.
    */ obj = [[NSObject alloc] init]; NSLog(@"obj reference count: %li", [obj retainCount]); */ [obj release];Copy the code
    #import "NewObject.h" @implementation NewObject -(NSObject *)newMyObj{ NSObject *obj = [[NSObject alloc] init]; return obj; } @implementation ViewController - (void)viewDidLoad {/** * ①} @end #import "viewController. h" #import" newObject. h */ NewObject *newObj = [[NewObject alloc] init]; */ NSObject *obj2 = [newObj newMyObj]; /** obj2 = [newObj newMyObj]; //obj2 retainCount=1 NSLog(@"obj2 reference count: %li", [obj2 retainCount]); Obj2 */ [obj2 release]; [newObj release]; } @endCopy the code
    • Methods other than alloc, new, copy, and mutableCopy, such as NSMutableArray’s array method, generate objects that are not generated by themselves. However, the variable does not hold the object from the object they generate, and we need to manually hold the object via retain.
    /** * ② Get the object that you didn't create, but you don't own it. * If you want to hold, you can hold it yourself by calling the retain method. */ NSMutableArray *arr = [NSMutableArray array]; /** * ④ Cannot release objects that are not owned by us * that is, we cannot release objects that are not owned by us. */ // [arr release]; /** * retain can hold the object */ [arr retain]; /** * The class array method returns an object from the pool automatically, and the * retain method holds the object ourselves, so the retainCount of arR =2 */ NSLog(@"arr reference count is: %li", [arr retainCount]); /** * () */ [arr release];Copy the code
    • Call the [NSMutableArray Array] method to make the obtained object exist, but do not hold the object itself. Internally, alloc generates and holds the object, and then adds the object to the automatic release pool by calling the autoRelease method. The objects we retrieve from array are actually released from the auto-release pool. When the pool is released, depending on whether the current iteration of the Runloop ends or the application exits, the release method is automatically called.

    Objects generated and held by alloc, new, copy, or mutableCopy, or held by retain, need to be released when they are not needed.

    • The autoRelease pool creates an NSAutoreleasePool object during each iteration of the main NSRunLoop program, which is destroyed at the end of the iteration. So we don’t have to create NSAutoreleasePool objects in MRC, but if we generate a lot of AutoRelease objects during this iteration, we will run out of memory. For example, the for loop reads a large number of images, resulting in a large number of Autorelease objects and running out of memory.
    for(int i = 0; i < 10000; I++){/** ** reads images and produces a large number of autorelease objects. * Because the NSAutoreleasePool object was not discarded, * ended up severely out of memory. * /}Copy the code

    Here we can solve the problem of memory inflation by generating an NSAutoreleasePool object.

    for(int i = 0; i < 10000; i++ ){ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; /** * Every time a pool object is created, an autoRelease object is generated by reading in an image. */ [pool drain]; /** * Autorelease objects are released together by calling [pool drain]. */}Copy the code

    If more than one NSAutoreleasePool object is nested, the object that called the AutoRelease method is added to the nearest NSAutoreleasePool object.

  1. Memory management under ARC

The compiler and runtime libraries automatically help us handle the relevant parts of reference counting. Also follow the principles of memory management. Only ARC is documented through the ownership modifier.

  • Ownership modifiers (ownership modifiers must be attached to the ID type or object type)
    1. __strong modifier: The default ownership modifier, that is, a variable holding a strong reference to an object (reference count +1) is nullified when it is out of scope and the object is discarded because it is not strongly referenced.
    2. __weak modifier: Holds a weak reference to an object (the reference count does not +1), which is equivalent to taking the object, but not holding it. In addition, when the object held by this weak reference variable is discarded, the variable is automatically invalidated and assigned to nil. Usually used to solve the problem of circular references.
    3. __unsafe_unretained modifier: a strong reference (that is, the reference count does not +1) is retained. A pointer attached with the __unsafe_unretained modifier points to the address of the original object after being discarded. Therefore, a dangling pointer is easy to occur.
    4. The __autoreleasing modifier is equivalent to calling autoRelease when ARC fails, that is, the object is registered with autoreleasepool, and the reference count is +1.


    Uintptr_t _objc_rootRetainCount(id obj);

    • __strong modifier (can be omitted by default)
    {/** * generate and hold the object itself. */ __strong obj = [[NSObject alloc] init]; // The reference count is 1 NSLog(@" reference count: %lu", _objc_rootRetainCount(obj)); }// after the variable obj goes out of scope, the strong reference is invalid and the owner of the object does not exist, so the object is discardedCopy the code
    {/** * objects that are not generated by themselves can also be held by themselves. */ __strong arr = [NSMutableArray array]; NSLog(@" reference count: %lu", _objc_rootRetainCount(obj)); NSLog(@" reference count: %lu", _objc_rootRetainCount(obj)); }// after the variable arr goes out of scope, the strong reference fails and the owner of the object does not exist, so the object is discardedCopy the code
    {// obj1 generates and holds object A id __strong obj1 = [[NSObject alloc] init]; Obj2 = [[NSObject alloc] init]; id __strong obj3 = nil; /** * obj1 holds a strong reference to object B from obj2. * Because obj1 is reassigned, the previously held strong reference to object A is invalid. Object A */ obj1 = obj2; Obj3 = obj1; NSLog(@" reference count: %lu", _objc_rootRetainCount(obj2)); /** * A strong reference to object B is invalid because nil is assigned to the variable obj1. */ obj1 = nil; /** * A strong reference to object B is invalid because nil is assigned to the variable obj2. */ obj2 = nil; /** * A strong reference to object B is invalid because nil is assigned to the variable obj3. * If no variable has a strong reference to object B, object B is discarded. */ obj3 = nil; }Copy the code

    To sum up: you can "release when you no longer need your own object" (discard variables with the __strong modifier) by terminating the scope of a variable, discarding the object to which a member variable belongs, or assigning a variable to nil.



    Disadvantages: Prone to cyclic references, resulting in memory leaks. (Memory leak: Objects that should be deprecated continue to exist beyond their production cycle.) eg:

    @interface Test : NSObject
    {
      id __strong obj_;
    }
    -(void)setObject:(id __strong)obj;
    @end
    
    @implementation Test
    -(void)setObject:(id __strong)obj{
      obj_ = obj;
    }
    @end
    Copy the code
    A Test *test1 = [[Test alloc] init]; Test *test2 = [[Test alloc] init]; Test2 [test1 setObject:test2]; test1 setObject:test2]; Test1 [test2 setObject:test1]; test2 setObject:test1]; } /** * out of scope, the strong reference of the test1 variable is invalid, and the strong reference of the test2 variable is invalid. * A strong reference to Test A is only obj_ of Test B, * A strong reference to Test B is only obj_ of Test A. * that is, the object is not released after the variable is out of scope. * [test1 setObject:test1]; This is a self-circular reference situation. The solution is the * __weak modifier, which comes next. * /Copy the code
    • __weak modifier
    id __weak obj = [[NSObject alloc] init];
    Copy the code

    The code will compile with an error, the variable holds a weak reference to the object, and the generated object will be released immediately even if you assign an object you created and hold to the variable. We can assign an object we generate and hold to a variable with the __strong modifier, and then assign that variable to a variable with the __weak modifier. As follows:

    id __weak obj2 = nil; { id __strong obj1 = [[NSObject alloc] init]; // obj2 variable holds a weak reference to the generated object obj2 = obj1; NSLog(@" in scope :%@",obj2); NSLog(@"obj reference count: %lu", _objc_rootRetainCount(obj)); // There is only one strong reference to this object, and the reference count is 1. /** ** Ob2 is a weakly referenced variable and does not add +1 to the reference count, but it is retrieved from the auto-release pool. * Across objc_loadWeaknessRetained an object with the __weak modifier. * The object is then registered to the automatic release pool through the objc_autorelease function. This ensures that the weak reference variable will not be released during the use of the weak reference variable and that it will not affect the actual reference count of the pair. 2 */ NSLog(@"obj2's reference count: %lu", _objc_rootRetainCount(obj2)); } NSLog(@" out of scope :%@",obj2); /** * Because the variable obj1 is out of scope, its strong reference is invalid. * The generated object has no strong reference variable to point to it, so it is discarded. * Because the object is deprecated, the weak reference to the variable obj2 that holds a weak reference to the object is invalidated and assigned to nil. * /Copy the code

    Because variables with an __weak modifier do not hold a strong reference to the object, objects are discarded beyond the scope of their strong reference variable, using this feature we can solve the problem of circular references. For example, the example of circular reference above can be modified as:

    @interface Test : NSObject
    {
      id __weak obj_;
    }
    -(void)setObject:(id __strong)obj;
    @end
    Copy the code

    Conclusion: The __weak modifier provides two functions: ① If the object to which the __weak modifier is attached is deprecated, then nil is assigned to the variable. ② Using a variable with an __weak modifier is using an object registered in Autoreleasepool.

    • __unsafe_unretained modifier
    id __unsafe_unretained obj2 = nil; __strong obj1 = [[NSObject alloc] init]; // the obj2 variable holds neither a strong reference nor a weak reference to an object. NSLog(@" in scope :%@",obj2); NSLog(@"obj2's reference count: %lu", _objc_rootRetainCount(obj2)); } NSLog(@" out of scope :%@",obj2); /** * Because the variable obj1 is out of scope, its strong reference is invalid. * The generated object has no strong reference pointer to it, so it is discarded. * An error is reported when accessing a deprecated object because the object is deprecated! (Dangling pointer) */Copy the code

    Result output:

    Within scope :<NSObject: 0x753E180 > /** indicates that after scope, the pointer still points to the address of the original object, that is, the variable is set to nil. <NSObject: 0x753e180>Copy the code
    • __autoreleasing modifier
    @autoreleasepool{// equivalent to NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id __autoreleasing obj1 = obj; // equivalent to [obj autorelease]; }// equivalent to [pool drain];Copy the code
    { id __strong obj = [[NSObject alloc] init]; @autoreleasepool { id __autoreleasing o = obj; /** * obj has a strong reference to obj, and o has a strong reference to obj, so NSLog(@) has a strong reference to obj, and NSLog(@) has a strong reference to obj, so NSLog(@) has a strong reference to obj. %lu", _objc_rootRetainCount(obj)); */ NSLog(@" reference count: %lu", _objc_rootRetainCount(obj)); */ NSLog(@" reference count: %lu", _objc_rootRetainCount(obj)); }Copy the code

    The __autoreleasing modifier, which we do not normally display, is added for ourselves in the following cases

    1. useWhen a variable with an __weak modifier is attached, we are actually accessing an object registered in Autoreleasepool. The auto-release pool has a strong reference to the object, and the objc_release() method is called immediately after it is used so as not to affect the object’s reference count.
      id __weak obj1 = obj;
      NSLog(@"class=%@",[obj1 class]);
      Copy the code

      Is equivalent to

      id __weak obj1 = obj;
      id __autoreleasing temp = obj1;
      NSLog(@"class=%@",[temp class]);
      Copy the code
    2. Objects as except alloc/new/copy/mutableCopy method return values, we finally is registered to the object in the autoreleasepool access. For example, the NSMutableArray class method array returns an object registered in AutoReleasepool.
    3. The __autoreleasing modifier is appended to Pointers of type ID or objects that are not specified for display. eg:NSError **pError; Equivalent to NSError * __autoreleasing *pError;
  • Note: An automatic variable with any of the __strong, __weak, and __autoreleasing modifiers initializes to nil.
id __strong obj1; // equivalent to id __strong obj1 = nil; id __weak obj2; // equivalent to id __weak obj2 = nil; id __autoreleasing obj3; __autoreleasing obj3 = nil;Copy the code
  • Rules that must be followed for ARC to be valid:

    1. Cannot use retain, release, retainCount, autorelease.
    2. NSAllocateObject and NSDeallocateObject cannot be used.
    3. The method naming rules for memory management must be followed.
    4. Do not call dealloc explicitly. (An explicit call to [super dealloc] is required in the dealloc method when ARC is invalid.)
    5. Use the @Autoreleasepool block instead of NSAutoreleasePool.
    6. Cannot use zones (NSZones).
    7. Object variables cannot be members of C language constructs. Because there is no way to manage the life cycle of a structure member in the C language specification. Memory management on ARC is done jointly by the compiler and the runtime library, and local variables in C are managed using the scope of that variable.
    8. Explicitly convert id and void *. Converting the two implicitly is fine in MRC cases, but ARC cases can cause compilation errors. __bridge or __bridge_retained/__bridge_transfer is required to display the conversion, where __bridge is prone to dangling Pointers.
    • __bridge: The conversion does not change the holding state of the object, prone to dangling Pointers or memory leaks.
    id obj = [[NSObject alloc] init];
    void *p = (__bridge void *)obj;
    id o = (__bridge id)p;
    Copy the code
    • __bridge_retained: Conversions are similar to retain, allowing the variable to be converted to an assignment to also hold the assigned object.
    id obj = [[NSObject alloc] init];
    void *p = (__bridge_retained void *)obj;
    Copy the code

    The above source code is equivalent if ARC is invalid

    id obj = [[NSObject alloc] init];
    void *p = obj;
    [(id)p retain];
    Copy the code
    • __bridge_transfer: A transformation is similar to release in that the object held by the converted variable is released after the variable is assigned to the target variable of the transformation.
    id obj = (__bridge_transfer id)p;
    Copy the code

    The above source code is equivalent if ARC is invalid

    id obj = p;
    [obj retain];
    [(id)p release];
    Copy the code
    • OC objects and Core Foundation objects can be converted using toll-free Bridge, namely CFBridgingRetaion and CFBridgingRelease. The function is implemented as follows:
    CFTypeRef CFBridgingRetain(id x){
       return (__bridge_retained CFTypeRef)x;
    }
    
    id CFBridgingRelease(CFTypeRef x){
       return (__bridge_transfer id)x;
    }
    Copy the code

    Eg: NSMutableArray objects generated and held are treated as Core Foundation objects.

    CFMutableArrayRef cfObj = NULL; {// create and hold a strong reference id obj = [[NSMutableArray alloc] init]; cfObj = CFBridgingRetain(obj); // The final reference count is: 2 printf("retain count:%d\n",CFGetRetainCount(cfObj)); Printf ("retain count after the scope = :%d\n",CFGetRetainCount(cfObj)); // CFRelease is deprecated because its reference count is 0. CFRelease(cfObj);Copy the code

    Use __bridge instead of CFBridgingRetain for conversion

    CFMutableArrayRef cfObj = NULL; {// create and hold a strong reference id obj = [[NSMutableArray alloc] init]; cfObj = (__bridge CFMutableArrayRef)obj; Printf ("retain count:%d\n",CFGetRetainCount(cfObj)); } /** * a strong reference to the out-of-scope variable obj is invalid, so the reference count is 0. */ printf("retain count after the scope = :%d\n",CFGetRetainCount(cfObj)); CFRelease(cfObj);Copy the code

    Eg: Objects generated and held by Core Foundation apis are treated as NSMutableArray objects.

    {// generate and hold the object CFMutableArrayRef cfObj= CFArrayCreateMutable(kCFAllocatorDefault,0, NULL); Printf ("retain count:%d\n",CFGetRetainCount(cfObj)); id obj = CFBridgingRelease(cfObj); /** * obj holds the object strongly because it is assigned to a variable with the __strong modifier, and it frees the object before it because CFBridgingRelease is called. Printf ("retain count after the cast = :%d\n",CFGetRetainCount(cfObj)); }// after going out of scope, the strong reference to the variable obj is invalid and the object is discarded because it has no owner.Copy the code

    Use __bridge instead of CFBridgingRelease for the conversion

    {// generate and hold the object CFMutableArrayRef cfObj= CFArrayCreateMutable(kCFAllocatorDefault,0, NULL); Printf ("retain count:%d\n",CFGetRetainCount(cfObj)); id obj = (__bridge CFMutableArrayRef)cfObj; /** * Obj is strongly holding the object because it is assigned to a variable with the __strong modifier. * Core Foundation apis are used to generate and hold the object, and obj is strongly holding the object. 2 */ printf("retain count after the cast = :%d\n",CFGetRetainCount(cfObj)); } /** * When the object is out of scope, the strong reference of the variable obj fails and the object is released, but the object generated and held by the Core Foundation API still exists, and the reference count is still: 1, * so the memory leak occurs. * /Copy the code
  • Property and ownership modifier correspondence

    1. Assign corresponds to the __unsafe_unratained modifier.
    2. Copy corresponds to the __strong modifier (but assigns the object to be copied).
    3. Retain corresponds to the __strong modifier.
    4. Strong corresponds to the __strong modifier.
    5. Unsafe_unretained corresponds to the __unsafe_unretained modifier.
    6. Weak corresponds to the __weak modifier.
    • Properties in property declarations are consistent with ownership modifiers. Such as:
    @property (nonatomic, weak) id obj;
    Copy the code

    This code will report a compilation error because the strong reference variable is assigned the weak attribute. Should be changed to:

    @property (nonatomic, weak) id __weak obj; // @property (nonatomic, strong) id obj;Copy the code