The basic concept

ARC is for automatic reference counting. The essence of reference counting memory management has not changed. ARC only handles the relevant parts of reference counting automatically.

At compile time, you can set ARC to be valid or invalid. ARC is valid in the default project. If not, specify the compiler property -fno-objc-arc.


Memory management way of thinking

Same as reference counting

  • Self generated objects, own
  • Objects that are not generated by themselves can be held by themselves
  • Release the objects they hold when they are not needed
  • Objects that are not owned by you cannot be released

Ownership modifier

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

__stong modifier

The __strong modifier is the default ownership modifier for ID types and object types. That is, the ID variables in the following code are actually treated with ownership modifiers

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

If ARC is invalid, it is written in the same way as if ARC is valid, but

{
    id __strong obj = [[NSObject alloc] init];
}
Copy the code

The above code explicitly specifies the scope of the variable.

When ARC is invalid, the source code can be written as follows:

/** ARC invalid */ {id obj =[[NSObject alloc] init]; [obj release]; }Copy the code

To release generated and held objects, add code that calls the release method. The source code behaves exactly as it would when ARC was in effect.

As shown above, the variable obj with the __strong modifier frees its assigned object when it is out of scope, i.e. when the variable is deprecated.

The __strong modifier represents a “strong reference” to an object. Variables that hold strong are discarded when they are out of scope, although strong references are invalidated and the referenced objects are released.

{id __strong obj = [[NSObject alloc] init]; /** If obj is out of scope, it will automatically release the object it holds. * The owner of the object does not exist because the object is discarded. * /Copy the code

To get an object that you did not generate and hold, the code looks like this:

*/ {id __strong obj = [NSMutableArray array]; /** If obj is out of its scope, it automatically releases the object it holds */Copy the code

The variable is assigned as follows:

id __strong obj0 = [[NSObject alloc] init]; /** object A*/ **obj0 holds A strong reference to object A*/ id __strong obj1 = [[NSObject alloc] init]; /** object B*/ **obj1 holds a strong reference to object B*/ id __strong obj2 = nil; /**obj2 does not hold any objects */ obj0 = obj1; /* *obj0 holds A strong reference to object B assigned by obj1 * Because obj0 is assigned, the strong reference that originally held object A is invalid. * The owner of object A does not exist because object A is discarded. * At this point, the variables holding strong references to object B are obj0 and obj1 */ obj2 = obj0; /* *obj2 holds a strong reference to object B assigned by obj0 * the variables that hold a strong reference to object B are obj0, obj1, and obj2; */ obj1 = nil; /* * A strong reference to object B is invalid because nil is assigned to obj1. * At this point, the variables holding strong references to object B are obj0 and obj2; */ obj0 = nil; /* * A strong reference to object B is invalid because nil is assigned to obj1. * At this point, the variables holding strong references to object B are obj0 and obj2; */ obj2 = nil; /* * A strong reference to object B is invalid because nil is assigned to obj2. * Discard object B. */ because the owner of object B does not existCopy the code

Variables with the __strong modifier, not only in variable scope, but also in assignment, can properly manage the owner of their object. Similarly, you can use variables with the __strong modifier attached to the arguments of a method.

@interface Test : NSObjecgt
{
    id __strong obj_;
}
- (void) setObject:(id __strong)obj;
@end

@implementation Test
- (id) init
{
    self = [super init];
    return self;
}

- (void) setObject:(id __strong)obj
{
    obj_ = obj;
}
@end
Copy the code

Use this class as follows:

{
    id __strong test= [[Test alloc] init]; / * *testHolds a strong reference to the Test object */ [test setObject:[[NSObject alloc] init]; /* * Obj_ member of the Test object, * holds a reference to the NSObject object; */} /* * becausetestThe variable is out of scope and the strong reference is invalid, so the Test object is automatically released. * The owner of the Test object does not exist, so the object is discarded. * When the Test object is deprecated, the obj_ member variable of Test is also deprecated, and the NSObject object is automatically freed. * The owner of the NSObject object does not exist, so the object is discarded. * /Copy the code

In addition, __strong, like __weak __autoreleasing, ensures that automatic variables with these modifiers are initialized to nil.

With the __strong modifier, you don’t have to retain or release again to think about reference counting memory management.

  • Self-generated objects are held by themselves
  • Objects that are not generated by themselves can be held by themselves
  • Release when you no longer need to hold objects yourself
  • Objects that are not owned by you cannot be released

__weak modifier

The __strong modifier alone does not solve the “circular reference” problem in reference counting memory management.

Such as:

@interface Test : NSObjecgt
{
    id __strong obj_;
}
- (void) setObject:(id __strong)obj;
@end

@implementation Test
- (id) init
{
    self = [super init];
    return self;
}

- (void) setObject:(id __strong)obj
{
    obj_ = obj;
}
@end
Copy the code

The following is a circular reference:

{
    id test0 = [[Test alloc] init]; /* Object A */ /* *test0 holds A strong reference */ ID of the Test object Atest1 = [[Test alloc] init]; /* Object B */ /* *test1 holds a strong reference to Test object B */ [test0 setObject:test1]; /* * The obj_ member variable of Test A holds A strong reference to Test B * The variables that hold A strong reference to Test B are obj_ and Test Atest1 * / /test1 setObject:test0]; /* * The obj_ member variable of Test B holds A strong reference to Test A * The variables that hold A strong reference to Test A are obj_ and Test Btest0 */} /* * becausetestThe 0 variable is out of its scope and the strong reference is invalid, so the Test object A * is automatically freed becausetest1 The variable is out of its scope, and the strong reference fails. Therefore, Test object B * is automatically released. The variable holding the strong reference of Test object A is obj_ *Copy the code

Circular references are prone to memory leaks. A memory leak is when an object that should be deprecated continues to exist beyond its lifetime.

The following code can also cause memory leaks (strong references to itself)

id test = [[Test alloc] init];
[test setObject:test];

Copy the code

Circular references can be avoided by using the __weak modifier.

The __weak modifier, in contrast to the __strong modifier, provides weak references. Weak references cannot hold object instances.

id __weak obj = [[NSObject alloc] init];
Copy the code

If you run the above code, the compiler will issue a warning.

Assigning retained object to weak variable; object will be released after assignment
Copy the code

The code above assigns the object it generates and holds to the variable obj with the __weak modifier attached. That is, the variable obj holds a weak reference to the holding object. Therefore, in order not to save the generated and held objects in their own state, the generated objects are released immediately. If an object is assigned to a variable with the __strong modifier and then to a variable with the __weak modifier, no warning occurs.

__strong obj0 = [[NSObject alloc] init]; */ id __weak obj1 = obj0; /** the obj1 variable holds a weak reference to the generated object */} /** Because the obj0 variable is out of scope, the strong reference is invalid, so it automatically frees the object it holds. * Discard the object because its owner does not exist. * /Copy the code

Because variables with an __weak modifier (weak references) do not hold objects, objects are released when their variable scope is outgrown. The following code avoids circular references.

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

The __weak modifier has another advantage: when holding a weak reference to an object, if the object is deprecated, the weak reference is automatically invalidated and in a state where nil is assigned (empty weak reference). As follows:

id __weak obj1 = nil; __strong obj0 = [[NSObject alloc] init]; /** Hold object */ obj1 = obj0; /**obj1 holds a weak reference to the object */ NSLog(@"A: %@",obj1); /** If obj0 is out of scope and strong references are invalid, it automatically frees its own objects. * Discard the object because it has no owner. * When an object is discarded, the weak reference of the obj1 variable that holds a weak reference to that object is invalidated, and nil is assigned to obj1. */ NSLog(@"B: %@",obj1); /** Outputs the assignment to nil in the obj1 variable */Copy the code

This code runs as follows:

2017-12-05 20:13:28.458858+0800 ImageOrientation[6316:1604800] A: <NSObject: 0x604000207710>
2017-12-05 20:13:30.112086+0800 ImageOrientation[6316:1604800] B: (null)
Copy the code

Above, use the __weak modifier to avoid circular references. You can determine whether the assigned object is deprecated by checking if the variable of the __weak modifier is nil.

__unsafe_unretained modifier

The __unsafe_unretained modifier is a variable deployed with the compiler’s memory management object. (ARC style memory management compiler work)

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

If you run the above code, the compiler will issue a warning. Even though the unsafe variable is used, the compiler does not ignore it.

Assigning retained object to unsafe_unretained variable; object will be released after assignment

Across variables, the __unsafe_unretained modifier is the same as the __weak modifier. As a result, generated and retained objects cannot be retained as our own. Therefore, generated objects are released immediately.

id __unsafe_unretained obj1 = nil; __strong obj0 = [[NSObject alloc] init]; /** Hold object */ obj1 = obj0; /** Although the obj0 variable is assigned to obj1, the obj1 variable holds neither a strong reference to the formation nor a weak reference to the object */ NSLog(@)"A: %@",obj1); /** If obj0 is out of scope and strong reference is invalid, it automatically frees the object it holds. * Discard the object because it has no owner. */ NSLog(@"B: %@",obj1); * * The object represented by the obj1 variable is deprecated (dangling pointer) * points to an object that once existed but no longer exists. Such Pointers are called dangling Pointers. The results are undefined, often resulting in program errors, and are difficult to detect. * Error access */Copy the code

This code runs as follows:

2017-12-06 08:45:54.005966+0800 ImageOrientation[6859:1736666] A: <NSObject: 0x604000011F00 > Run to NSLog(@"B: %@",obj1), Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)Copy the code

__autoreleasing modifier

Neither the AutoRelease method nor the NSAutoreleasePool class can be used when ARC is in effect. Although autoRelease cannot be used directly, the AutoRelease function is useful.

ARC is invalid with the following code:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autoreleae];
[pool drain];
Copy the code

When ARC is in effect, the code could look like this:

@autoreleaepool{
    id __autoreleasing obj = [[NSObject alloc] init];
}
Copy the code

Specify the @AutoReleasepool block instead of the NSAutoreleasePool class object generation, hold, and discard scope.

Also, when ARC is in effect, instead of calling the autoRelease method, assign the object to a variable with the __autoreleaing modifier attached. Assigning an object to a variable with the __autoreleasing modifier is equivalent to calling the object’s autorelease method when ARC is invalid, i.e. the object is registered with autoreleasepool.

This is interpreted as replacing the NSAutoreleasePool class with the @AutoReleasepool block and the autorelease method with a variable with the __autoRELEASING modifier when ARC is in effect.

In general, the __autoreleasing modifier is not appended explicitly. In the generated and hold objects, little can use alloc/new/copy/mutableCopy method to achieve object, but the object has been registered by autoreleasepool. This is because the compiler will check whether the method name begins with a alloc/new/copy/mutableCopy, if not automatically registered to autoreleasepool returns the value of the object. (Objects returned by the init method are not registered with Autoreleasepool.)

@autoreleasepool{/** get objects that are not generated and held */ id __strong obj = [MutableArray array]; /* * because the variable obj is a strong reference, it holds the object itself. * And the object is automatically registered with autoreleasepool */} /* * Because the variable obj is out of scope, the strong reference is invalid, so it automatically releases the object it holds. * Also, with the end of the @Autoreleasepool block, all objects registered in Autoreleasepool are automatically released. * Discard an object because its owner does not exist. * /Copy the code

To access a variable with an __weak modifier, you must access an object registered with Autoreleasepool.

id __weak obj1 = obj0;
NSLog(@"class = %@",[obj1 class]);
Copy the code

Is equivalent to

id __weak obj1 = obj0;
id __autoreleasing temp = obj1;
NSLog(@"class = %@",[temp class]);
Copy the code

Because the __weak modifier holds only weak references to the object, the object may be discarded during access to the reference object. If you want to register an object you access with Autoreleasepool, you can ensure that the object is loved until the end of the @Autoreleasepool block. Therefore, shying must be registered with objects in autoReleasepool when using variables with an __weak modifier.

Similarly, Pointers to ids or objects that are not specified for display are appended with the __autoreleasing modifier.


Rules of the ARC

To compile source code with ARC in effect, the following rules must be followed:

  • Cannot use retain/release/retainCount/autorelease
  • Cannot use the NSAllocateObject/NSDeallocateObject
  • The method naming conventions for memory management must be followed
  • Do not display calls to dealloc
  • Use the @Autoreleasepool block instead of NSAutoreleasePool
  • Unusable areas (NSZone)
  • Object variables cannot be members of C struct/union
  • Display conversion “id” and “void”

Cannot use retain/release/retainCount/autorelease

Memory management is the job of the compiler, because it is not necessary to use the memory management methods (retain/release/retainCount/autorelease). Can only be used if ARC is invalid and memory management is done manually.

Cannot use the NSAllocateObject/NSDeallocateObject

OC objects are typically generated and held by calling the Alloc class method of the NSObject class. The alloc class methods actually generate and hold objects by calling the NSAllocateObject function directly.

The method naming conventions for memory management must be followed

When the ARC is invalid, is used to generate/hold objects must follow the following naming conventions: begin with alloc/new/copy/mutablCopy method when the return object, must be returned to the calling party shall be held by the object.

When ARC is in effect, add the init rule in addition to the above rule.

Begin with the init method rules than alloc/new/copy/mutableCopy more strictly. The method must be an instance method and must return an object. The object returned should be of type ID or the object type of the class declared by the method.

id obj = [[NSObject alloc] init];

As shown above, the init method initializes the object returned by the alloc method and returns it to the caller intact.

Note: Initialize is not included in the above naming rules.

Do not call dealloc explicitly

Whether ARC is valid or not, an object is discarded as long as none of its owners hold it. When an object is deprecated, its dealloc method is called whether ARC is valid or not. When ARC is invalid, you must call [super Dealloc]. When ARC is in effect, it follows the rule that there is no explicit call to Dealloc, which ARC takes care of automatically. Dealloc does what is necessary when only the technology is deprecating an object, such as deleting a registered agent or observer object.

Use the @AutoReleasepool block instead of NSAutoreleasePoll

Unusable areas (NSZone)

Object variables cannot be members of C language constructs

struct Data{
    NSMutableArray * array;
}
Copy the code

The compiled

error:ARC forbids Objective-C objects in struct

There is no way to manage the lifetime of structure members in the C protocol.

To place an object variable in a struct member, cast it to void* or attach the __unsafe_unretained modifier. (A variable with the __unsafe_unretained modifier does not belong to the compiler’s memory management object)

Explicitly convert id and void*

When ARC is invalid, the id and void* variables can be assigned to each other without problems, but when ARC is valid, it causes a compilation error.

When ARC is in effect, specific conversions are required when an ID or object variable is assigned to void* or when it is assigned backwards. If you want to assign purely, you can use the __bridige conversion.

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;
Copy the code

But a __bridge conversion to void* is just as secure as, or even less secure than, assigning the __unsafe_unretained modifier. If you manage without paying attention to the owner of an assignment object, your program will crash due to dangling Pointers.

There are two other __bridge conversions: __bridge_retained and __bridge_transfer

The __bridge_retained conversion allows the variable to be converted and assigned to also hold the assigned object. The __bridge_retained transformation is similar to retain.

void *p = 0;
{
    id obj = [[NSObject alloc] init];
    p = (__bridge_retained void *)obj;
}
NSLog(@"class = %@",[(__bridge id)p class]);
Copy the code

Run: 2017-12-06 16:39:06.148058+0800 ImageOrientation[7685:2312365] class = NSObject

At the end of the variable scope, the object is released as the variable obj holding a strong reference becomes invalid, but the __bridge_retained transformation makes p appear to hold the state of the object, so the object is not discarded.

The code to implement the above logic when ARC is invalid is:

void *p = 0; { id obj = [[NSObject alloc] init]; /** [obj retainCount] ->1 */ p = [obj retain]; /** [obj retainCount] ->2 */ [obj release]; /** [obj retainCount] ->1 */ ** [(id)p retainCount] ->1 * obj retainCount ->1 */Copy the code

The __bridge_transfer conversion, in which the object held by the converted variable is released after the variable is assigned to the converted target variable. The __bridge_transfer transformation is similar to release.

id obj = (__bridge_transfer id)p; If ARC is invalid, the code is as follows:

/**ARC invalid */ id obj = (id)p; [obj retain]; [p release];Copy the code

__bridge translation, __bridge_retained translation, and __bridge_transfer translation are commonly used for translation between OC and CoreFoundation objects.

  1. __bridge: the conversion of CF and OC objects involves only the type of the object, not the ownership of the object.
  2. __bridge_transfer: When CF objects are converted into OC objects, the ownership of CF objects is given to OC objects, and then ARC can automatically manage the memory. (same as CFBridgingRelease())
  3. __bridge_retained :(opposite to __bridge_transfer) it is commonly used to transfer the ownership of the OC to a CF object. (same effect as CFBridgingRetain())

The following functions can be used to convert between OC objects and CoreFoundation objects, known as toll-free Bridge “Free Bridge” conversions.

CFTypeRef CFBridgingRetain(id X){
    return (__bridge_retained CFTypeRef)X;
}

id CFBridgingRelease(CFTypeRef X){
    return (__bridge_transfer id)X;
}
Copy the code

The following example deals with the generated and held NSMutableArray object as a CoreFoundation object.

CFMutableArrayRef cfObject = NULL; {id obj = [[NSMutableArray alloc] init /** Variable obj holds a strong reference to the generated and held object */ cfObject = CFBridgingRetain(obj); /** Assign the object CFRetain to the variable cfObject */ CFShow(cfObject) via CFBridgingRetain;printf("retain count =%d\n",CFGetRetainCount(cfObject)); /** A strong reference to obj is invalid because obj is out of scope and the reference count is 1 */printf("retain count after the scope =%d\n",CFGetRetainCount(cfObject)); CFRelease(cfObject); /** The reference count is 0 because the CFRelease object is deprecated. * /Copy the code

The running results are as follows:

(
)
retain count =2
retain count after the scope =1
Copy the code

OC objects generated and held by the API of the Foundation framework can be used as CF objects or released by CFRelease. The code above can also be replaced with the __bridge_retained conversion.

CFMutableArrayRef cfObject = (__bridge_retained CFMutableArrayRef) obj;

If the __bridge conversion is used, the first sentence prints 1. Because the __bridge conversion does not change the holding status of the object, obj holds a strong reference to the NSMutableArray object, so it is 1. Obj is out of its scope, so the strong reference fails, the object is released, and the object without owner is discarded, so the dangling pointer appears, causing the crash.

So, take the CoreFoundation API and generate and hold the object, and treat it as an NSMutableArray object, as follows:

{
    CFMutableArrayRef cfObject = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
    printf("retain count =%d\n",CFGetRetainCount(cfObject)); /* * CoreFoundation API generates and holds object * after the object reference count is 1 */ id obj = CFBridgingRelease(cfObject); /* * The obj variable holds a strong reference to the object while the object is released via CFReleaseprintf("retain count after the cast =%d\n",CFGetRetainCount(cfObject)); /* * Since only the variable obj holds strong references to the generated and held object, the reference count is 1 *. In addition, after CFBridgingRelease, the pointer assigned to the variable cfObject also points to an existing object, so it works fine. */ NSLog(@"class =%@",obj); } /* * Because the variable obj is out of scope, its strong reference is invalid, the object is freed, and the object without owner is discarded. * /Copy the code

The running results are as follows:

Retain Count =1 Retain Count after the cast =1 2017-12-06 21:30:02.473804+0800 ImageOrientation[8249:2573155] class =()Copy the code

You can also use the __bridge_transfer conversion instead of CFBridgingRelease.

id obj = (__bridge_transfer id)cfObject;
Copy the code

If __bridge is used instead of the __bridge_transfer or CFBridgingRelease conversion, the first sentence is printed as 1 and the middle as 2, since both obj and cfObject hold a strong reference to the object. Out of range obJ strong references are invalidated and the object’s reference count is 1.


The ARC properties

The objective-C class attributes also change when ARC is in effect. The details are as follows:

Property Specifies the property of the declaration Ownership modifier
assign __unsafe_unretained modifier
copy __strong modifier (but assigns the object to be copied)
ratai __strong modifier
unsafe_unretained __unsafe_unretained modifier
weak __weak modifier

These properties are copied to the specified property as if they were assigned to a variable with the corresponding property modifier attached. Only the copy property is not a simple assignment, it is a copy of the object generated by the copy source via the copyWithZone: method of the NSCopying interface.