Swift’s ARC and Memory Leaks
Swift introduced a powerful feature called Automatic Reference Counting (ARC) which handles most of an app’s memory management. Yet beginner programmers often are unaware of how it functions.
ARC is responsible for allocating and deallocating the memory that objects use. To make this automatic, ARC counts the references to variables that you create.
Once the references to object are removed ARC will automatically deallocate object for you.
When ARC deallocates the memory, the object’s deinit() will be called.
In Swift, memory leaks exist with retain cycles. A retain cycle occurs when two objects hold strong references to each other.
This commonly happens with @escaping closures.
When writing an escaping callback, any using any of the current class’s methods or to self will create a strong reference to that instance creating a retain cycle since the closure would hold a strong reference to the class and the class would hold a strong reference to the class the closure is in.
The example below shows a common retain cycle when using an @escaping closure.
In the example above, the FeedViewController holds a strong reference to the NetworkHelper with the variable networkHelper. A reference to the FeedViewController is then passed in as a closure to the networkHelper creating a strong reference from networkHelper to the FeedViewController.
ARC will never get to cleaning up the FeedViewController or NetworkHelper even if no other references to theses objects exist. This will cause the two objects to exist until the user closes the app. If the FeedViewController is ever recreated, then this retain cycle could happen again making the issue even worse.
Weak and Unowned
To break the retain cycle you must remove the strong reference to the FeedViewController from the closure that is passed to networkHelper. This is done by using a weak or unowned self.
The weak keyword creates a weak reference to a variable by not incrementing ARC’s reference count. However, since it is not a strong reference, it is not guaranteed that the object will exist when the closure is executed. So anytime you use the weak keyword, the variable is optional.
The unowned keyword also does not increment ARC’s reference count, but it is also not optional. But since it doesn’t have a strong reference to the variable either it may not exist and may point to something else entirely. You shouldn’t use unowned unless both objects will always exist together like your computer and its processor.
To fix the example above you just need to specify that self is a weak reference and the retain cycle will be broken.
Debugging Memory Management
XCode has a great feature to check the memory usage, references, and instances of objects that exist as your app runs.
The Xcode Memory Graph pauses your apps execution and shows all objects that currently exist. You can also select an instance of an object and see what objects hold references to it.
When memory leaks do happen, Xcode often even highlights the troubled classes with a purple explanation point. Adding in a simple retain cycle and performing the action several times led to a memory graph that is shown below.
Clicking on one of the instance further reveal that a closure is holding a reference to the DetailsViewController where I created the following retain cycle.
So next time you create a closure, whether it be for an observer on NotificationCenter, a network call, or some other asynchronous task remember to check your self before you wreck your self.