iOS Swift Memory Optimisation
This post is a result of a work problem we had, one of our app was using a lot of memory and that usage was making the app very unstable. As I was researching this issue, I found a lot of posts and stack overflow answers but nothing consolidated. So, I thought of writing something that’ll give you an idea about where an app’s memory usage can be optimised.
This post will contain the issues I faced during our app development.
1. First thing I tackled was strong references in app
While researching I found about strong references and how delegates play a fairly big role in their creation. I came across this beautiful and concise post by NatashaTheRobot https://www.natashatherobot.com/ios-weak-delegates-swift/ (thanks Natasha!!).
While optimising, I came across lines like this
var customDelegate: CustomDelegate?
This property was holding a strong reference to my view controller, which prevented it from ever being deallocated, causing memory to climb fairly rapidly. After changing this reference to weak, solved much of the memory problem.
weak var customDelegate: CustomDelegate?
Also, I had a Fab class which helped in adding fab button to view controller, here I found a cycle where I was passing controller’s reference to Fab class and having a Fab reference in my view controller hence neither of them got deallocated, solved this problem with this line
weak var fabButton: Fab?
2. Strong references in closures
I tried to find other places of strong references and came across how closures affect app’s memory usage. I came across this post by kelan.io, this is a great post explaining how strong self reference in closure can prevent the controller from deallocating, now I was not a big fan of
guard in closure to unwrap weak self and did not wanted to risk using unowned as explained in this
Stack Overflow answer http://stackoverflow.com/a/24320474
The difference between
weakis declared as an Optional while
unownedis not. By declaring it
weakyou get to handle the case that it might be nil inside the closure at some point. If you try to access an
unownedvariable that happens to be nil, it will crash the whole program. So only use
unownedwhen you are positive that variable will always be around while the closure is around
To get rid of this, I came across keyword
@noescape while reading this answer http://stackoverflow.com/a/28428521
@noescapeguarantees that the closure will not be stored somewhere, used at a later time, or used asynchronously.
From the caller’s point of view, there is no need to care about the lifetime of captured variables, as they are used within the called function or not at all. And as a bonus, we can use an implicit
self, saving us from typing
Though I had to use weak self in asynchronous networking closures but thats Okay!
3. Other problems noticed
I noticed that timers actually held a strong reference to the controller and not allowing the controller to get deallocated, I found an article online which asked to invalidate the time in
dealloc method, but since timer has a strong reference
dealloc will never be called, so that didn’t work for me.
Hence, I invalidated the timer in
func viewDidDisappear(_ animated: Bool) and voilà it worked 🙌
While exploring I found people had memory problems when getting image from context and guess what I too had this line in my code
let outputImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
One of the solutions I found was http://stackoverflow.com/a/23875557, but fortunately for me I was able to remove the complete code piece.
Hope this helps :)