Memory management in Swift(Heap, Stack, ARC)
Swift uses stack or heap data structure to store object. Memory management refer to the allocation and deallocation of an object.
There are two memory management model in iOS.
- MRC : manual reference counting
- ARC: automatic reference counting
What is MRC?
Initially we are using non-arc i.e MRC(manual reference counting), we need to retain object and release the object manually.
In this model, we need to use retain
and release
keywords for retaining the object in memory and releasing that object from memory.
What is ARC?
Now swift use ARC to automatically allocating and deallocating memory. In this method, You don’t need to use release and retain.
The core concept of ARC is very simple, an object is retained in memory by incrementing the reference count and then released by decrementing that same count.
In swift with ARC, we mainly use strong, weak and unowned references.
If reference count become zero, that object will be deallocated from memory.
What are Object’s Lifetime?
- Allocation: Takes memory from a stack or heap.
- Initialization:
init
code runs. - Usage: The object is used
- Deinitialization:
deinit
code runs. - Deallocation: Returns memory to a stack or heap.
What are memory management issues?
- Freeing or overwriting data when object is in use. It causes crash or data corruption.
- Not freeing up the object if it is not in use. It leads memory leak.
- App crashes
What is memory leak?
When allocated memory for object is not freed even though object is never going to be used again, it is known as memory leak. It occurs, when two objects reference each other, making it impossible for either to get deallocated.
Memory leak in app causes poor system performance due to increasing amounts of memory.
What are main rules in memory management?
- when we create/own the object, subsequently release when they are no longer used again.
- Use retain count to release and retain the object in memory
- Don’t release object if you don’t own that object.
How object is retained and released in memory?
Strong references increases the retain count. retain count is 1 or greater than 1 means, object is allocated in memory and when retain count reaches zero
that object will be deallocated.
Consider above example, We have created two class person and department. Person object is created and assigned to variable p1
and department object is created and assigned to variable department
When person object is deallocated, automatically it deallocates it’s sub object as well. so department reference count will decrease to 1 when person object is deallocated.
when person object is deallocated, its deinit method is called but department object’s deinit method is not called because it’s Retain Count is not equal to zero
. after department is assigned to nil its Retain Count become zero and its deallocated from memory.
In above example, we made person
hold a reference to its department
and also department
reference its person
When person object is assigned to nil, It’s Retain Count become 1 and when department object is assigned to nil, its Retain Count = 1.
In this situation both object’s retain count is more than one and both objects reference each other, making it impossible for either to get deallocated. It leads to memory leak. This situation is called as retain cycle
When view controller is removed, all the view controller’s objects will be released. any object’s sub-objects will be released when they are released.
Note: if other classes have a strong reference to an object of a class, then the whole class won’t be released. So, it is recommended to use weak or unowned properties
What is retain cycle/Reference Cycles?
Retain cycle occurs when two objects reference each other, making it impossible for either to get deallocated — because both of their retain counts will always be one or greater.
How can you fix retain cycle?
Change one of the references to weak
or unowned
reference.
Weak Reference: It doesn’t increases the reference count. weak references are always declared as optional types. When reference count goes zero the object will be automatically set to zero.
Unowned References: It is same as weak references. It will not increase the retain count when object is referred. The main difference is, it never optional types. If you try to access an unowned property that refers to a deinitialized object, you’ll trigger a runtime error comparable to force unwrapping a nil
optional type.
In above example, you can resolve strong reference cycle by changing weak or unowned reference type in department class.
Strong Reference Cycle in Closures:
When you use closures within a class instance they could potentially capture self. If self, in turn, retains that closure, you’d have a mutual strong reference cycle between closure and class instance.
To avoid it, you’d use the same keywords weak and unowned in closure capture list.
To know more about closure and capture list please click here
How can we identify memory leaks?
Xcode has a built in memory graph debugger. It allows you to see how many reference counts you have on an object and which objects currently exist.
Heaps and Stacks
Swift automatically allocates memory in either the heap or the stack.
Stack:
- Static in memory and allocation happens only during compile time.
- stack is LIFO(Last in first out) data structure
- very fast access
- When a function is called, all local instances in that function will be pushed on to the current stack. And once the function has returned, all instances will have been removed from the stack.
- Data stored in the stack is only there temporarily until the function exits and causes all memory on the stack to be automatically deallocated.
- Each “scope” in your app (like the inner contents of a method) will provide the amount of memory it needs.
- stack is not used with objects that change in size.
- Each thread has its own stack
- Stacks store value types, such as structs and enums.
- If the size of your value type can be determined during compile time, or if your value type doesn’t recursively contains / is not contained by a reference type, then it will require stack allocation.
- Value type doesn’t increases retain count. But If your value type contains inner references, copying it will require increasing the reference count of it’s children instead.
Heap:
- Dynamic in memory and allocation happens during runtime.
- Values can be referenced at any time through a memory address.
- no limit on memory size
- slower access
- When the process requests a certain amount of memory, the heap will search for a memory address that fulfils this request and return it to the process.
- When the memory is not being used anymore, the process must tell the heap to free that section of memory.
- It requires thread safety.
- heap is shared with everybody
- If the size of your value type cannot be determined during compile time (because of a protocol/generic requirement), or if your value type recursively contains / is contained by a reference type (remember that closures are also reference types), then it will require heap allocation.
- class is stored in heap memory.
Heap Allocation is slower than Stack Allocation not just because of the more complex data structure — it also requires thread safety. Each thread has its own stack, but the heap is shared with everybody, demanding synchronization.
Interview tricky questions?
1. What happens when we execute below code?
Ans: Object will be allocated and deallocated immediately and you can’t refer that object again. Bellow is simple example:
2. Why IBOutlets are weak. What happen if you use strong references?
If you declare IBOutlets as strong or weak, your application won’t crash and burn. Every view controller keeps a reference to the view it manages. That reference is strong. The view should not be deallocated as long as the view controller is alive.
That VC’s view always keeps a strong reference to the subviews it manages. That makes sense because the subviews is still alive and visible even if we don’t declare an outlet for the subviews in the ViewController
class.
As per ARC: When the view controller is deallocated, the view it manages is deallocated as well. This also means that any subviews the view manages are also deallocated.
The view heirarchy already has a strong reference. so assigning weak to IBoutlets might help to avoid reference cycles when your dismissing the view controller.
As of 2015, recommended best practice from Apple was for IBOutlets to be strong unless weak is specifically needed to avoid a retain cycle.
Thank you for reading
This is my first article on Memory Management, I hope it will be useful for you. If you enjoyed it, feel free to hit the clap button below 👏 to help others find it! and follow me on Medium.