Understanding ARC
nitin vatsya
PSM Certified with Coupa Platform Certification Sr. Business Analyst and Technical Manager
“Automatic Reference Counting (ARC) helps in development by taking care of Reference Cycle, developer need not to worry about the releasing the objects of no use.”
The above statement looks very much helpful to the developer and gives a good feeling too. But there are some exception and restriction which creates problem for the developer if not having knowledge about these. However, this doesn't completely free developers from worrying about memory management on these platforms.
We will see and try to understand the exceptions later before we will go through the ARC concept to have a better knowledge.
Automatic Reference Counting (ARC)
It was invented in 1960 and has been used in Python and Mathematica, with Objective-C is firstly introduced along with Xcode4.2.
For an application it is required to release or deallocate an object/instance of class/ Enumerations or Structure when the same is no longer of use. When the object is released memory occupied by it becomes free, meaning that the same object is now not in existence. This is instrumental for an application and the OS as well.
ARC implements memory management automatically for Objective-C objects and blocks, freeing the programmer from the worries of retain and release activity. But it doesn't provide a cycle collector; users must explicitly manage the lifetime of their objects, breaking cycles manually or with weak or _unsafe _unretained references.
ARC may be explicitly enabled with the compiler flag -fno-objc-arc or -fobjc-arc.
ARC cares about Retainable object pointers. Here two things need to be clear is Pointer Values & Pointer Objects.
Pointer Values : Indicating the location of a pointed-to object and is a value of retainable type pointer.
Pointer Objects : Locations in memory which stores pointer values also called as retainable pointer. It is either a null pointer or a pointer to a valid object.
There are three king of retainable object pointer types :
- Block pointer (formed by applying the caret (^) declarator sign to a function type)
- Objective-C object pointers (id, Class, NSString, NSArray.....etc)
- typedefs marked with _attribute_(NSObject) -- (not recommended)
Other pointer types. Such as int* and CFStringRef, CGImageRef ... etc are not subject to ARC's semantics and restrictions.
In general, ARC does not perform retain or release operations when simply using a retainable object pointer as an operand within an expression. It means that -
- Loading a retainable pointer from an object with non-weak OWNERSHIP.
- Passing a retainable pointer as an argument to a function or method, and
- receiving a retainable pointer as the result of a function or method call.
A type is a retainable object owner type if it is a retainable object pointer type or any array type contains retainable object owner type.
Ownership Qualifier : It is a type qualifier which applier only to retainable object owner types. An array type is ownership-qualified according to its element type and adding an ownership qualifier to an array type so qualifies its element type.
There are four ownership-qualifiers :
- _unsafe_unretained
- _weak
- _autoreleasing
- _strong
Exceptions :
- Objective-C class doesn't supports _weak references. It is undefined behaviour to perform and operation with weak assignment semantics with a pointer to an Objective-C object.
- It is undefined behaviour to access an ownership-qualified object through an lvalue of a differently-qualified type. It is possible only if any non-weak (_weak) object is being read by an _unsafe_unretained lvalue.
- It is undefined behaviour if the storage of a _strong or _weak object is not properly initialized before the first managed operation is performed on the object. Storage for a _strong or _weak object may be properly deinitialized by assigning a null pointer into it.
- It does not end the lifetime of _strong variables when their scopes are abnormally terminated by any exception.
- It does not perform release which would occur at the end of a full-expression if that full-expression throws an exception.
- ARC will not be helpful with non-ObjC memory, for example if malloc() something is used, it still needs to free() it.
- ARC can be fooled by performSelector: if the compiler can't figure out what the selector is (the compiler will generate a warning on that).
- ARC will also generate code following ObjC naming conventions, so if you mix ARC and MRC code you can get surprising results if the MRC code doesn't do what the compiler thinks the names promise.
- Not invalidating NSTimers when dismissing view controllers
- Forgetting to remove any observers to NSNotificationCenter when dismissing the view controller.
- Keeping strong references to self in blocks.
- Using strong references to delegates in view controller properties.
- ARC will also not manage CoreFoundation types. You can 'bridge' them (Using CFBridgingRelease()) but only if you are going to use it as an Objective-C/Cocoa object. Note that CFBridgingRelease just decrements the CoreFoundation retain count by 1 and moves it to Objective-C's ARC.
Xcode 9 provides a great tool for finding that kind of issues. It is called: "Debug Memory Graph". Using it you can find your leaked object by class type and you can see clearly who holds a strong reference to it, by releasing it from there solves your problem. It is also detects memory cycles.
These requirements are followed automatically for objects whose initialization and deinitialization are under ARC control -
- Objects of static, automatic and temporary storage duration
- instance variables of Objective-C objects
- elements of arrays where the array object's initialization and deinitilization are under ARC
- field of Objective-C struct types where the struct object's are under control of ARC
- non-static data members of Objective-C++ non-union class types
- Objective-C++ objects and arrays of dynamic storage duration created with the new or new[] operators and destroyed with the corresponding delete or delete[] operators.
On the other hand some types of data structure are not possible with ARC or MRC while GC (garbage collector) can handle them.
For example,
A -> [B1, B2, B3] B1 -> A, B2 -> A, B3 -> A
Using ARC if finished with A it have a refcount of 3 (B1, B2 & B3) and these B val will have a reference count of 1. So all of those objects remain live even though nothing can ever use them.
To make sure that instances don’t disappear while they are still needed, ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.
To make sure that instances don’t disappear while they are still needed, ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.
Some PROPERTY definition
- STRONG / retain -
* It says “to keep in the memory heap until someone don't point it anymore”
* Use Strong only if it is required to retain the object.
* By Default all instance variables and local variables are strong type pointers.
* In general practice Strong is being used for UIViewControllers (UI item's parents)
* it denotes the OWNERSHIP.
* retain available in iOS4 & above while strong is available in iOS5 and above.
Example :
@property (strong, nonatomic) ViewController *viewController;
@synthesize viewController;
NOTE : ARC will automatically release weak objects when there are no pointers to them. Strong - is synonym to retain, so the object is retained and it is our responsibility to make the object nil
- WEAK -
* it says “to keep as long as someone else point to it strongly”
* useful to avoid retain cycles. Keeps value as Assign, no retain or release
* use weak if only pointer to the object without retaining it, is needed.
* In general IBOutlets (UIViewController's child) is declared as weak, because the child object only needs to exist as long as the parent object is there.
* It doesn't protect the referenced object from collection.
* Weak is essentially assign, an unretained property. Except when the object is deallocated the weak pointer us automatically set to nil.
Example :
@property (weak, nonatomic) IBOutlet UIButton *myButton;
@synthesize myButton;
When we use weak?
The only time you would want to use weak, is if you wanted to avoid retain cycles (e.g. the parent retains the child and the child retains the parent so neither is ever released). We can use it with bellow kind of objects -
- For UI objects,
- delegates ,
- blocks
NOTE : When ViewContorller is being presented iOS loads it's view hierarchy on screen (creating missing views). When other ViewController is presented, this first hierarchy of views is deallocated. But if you have 'strong' in ViewController, then this view can't be deallocated, when it's off screen. Which could have hard impact on device memory and cause of slowdown of app. (Of course device have a lot of memory and it would be fine upto 5-10 screen app, but in huge app you'll get into trouble)
- Retain (equals to strong)
* use Strong to retain an object.
* retain on assignment releases the old value and hold the new one.
* retain is the same as strong
* As per apple if retain is used it will be auto converted or work similar to Strong only.
* methods like alloc includes an implicit retain.
Example :
@property (nonatomic, retain) NSString *name;
@synthesize name;
- Assign
* It is the default one and simply performs a variable assignment.
* Use Assign for C primitives and Weak for weak referenced to Objective-C object.
* exactly similar to weak except it does not gets nil out the object if released.
Example :
@property (nonatomic, assign) NSString *address;
@synthesize address;
- Copy (Shallow copy)
* it is good practice to always set immutable properties to copy, Copy will ensure that it is dealing with an immutable objects only.
* if an immutable object is passed in, it will retain the object – but if a mutable object is passed in, it will copy it.
- Readonly
* used for disabling the setting of the properties
* prevents code from compiling if there is an infraction
* values can be changed what's delivered by the getter method by changing the variable directly using its instance type or within getter method itself.
- Nonatomic vs atomic
* nonatomic is faster than atomic
* Atomic property guarantees thread safety
* always use nonatomic unless having any specific requirement for atomic.
* Atomic only stalls accessing the property even when it is simultaneously being set by another thread. (i.e. Thread safe)