Managing memory safely inside closures

November 4, 2017

Memory management has been always an issue in the era before ARC, many of the current iOS devs. didn't have to get their hands dirty with retain, alloc and release messages, but still there are many use cases where memory leaks might occur, leading to an increased memory foot print or dangling pointers and eventually crashes, a common scenario   leaks often occur is within "Blocks" or as called in swift "Closures", where I will explain it and give some hints of how to avoid it in the next lines.

 

Memory Management Basics

 

Memory in iOS is managed through using concept of "Reference Counting" where memory chunks (corresponding to a class instance) are reserved to the app as long as there is at least one strong reference to it (e.g. through a property or a var), this is managed through ARC which holds the info about how many the number of references for this instance and increases or decreases the count accordingly and once the count reaches 0, the ARC frees up this memory chunk and make it available for use again.

 

 

Real-World Example

 

lets check this practically through the classical example of employee and company, so create a new playground and create two classes "employee" and "company

 

Now create an instance of Employee class, this makes the retainCount of this instance = 1

Create an instance Company class and add employeeA in its employeesArray, now employeeA Instance has 2 strong references to it and companyX instance has 1 strong reference.

now add a property to the Employee class , which would identify the company this employee belongs to 

and assign companyX to employeeA, this will make companyX has a retainCount of 2 

this is an example of a very common retain cycle, try now to wrap them in a do block (notice how the init and deinit logs are handy) and it will show that the instances are not deallocated, but only initialised.

 

Console output:

in order to resolve this cycle, just add weak or unowned beside the company property, personally I tend to use weak more often, since it is safer, you can read more about the weak and unowned here.

now notice the console output

Wooho, now everything works as expected and the cycle is resolved, hopefully this refreshed your memory about how memory management works generally in CocoaTouch, now lets jump to the main topic today which is about Closures.

 

Closures or Blocks

Blocks or Closures are awesome but unfortunately they are very good candidates for memory leaks, as there are 2 issues which normally could be faced :

 

1- RetainCycle: through having strong reference from the closure to the objects it encloses has a reference to the block already

e.g. add a lazy var block property to the employee class

then call block() from employeeA instance

 and notice from the console that 

doesn't get called anymore which means that there is a retain cycle here.

 

2- Memory Occupancy/ Wrong Behaviour: when an async call (e.g. GCD ) returns after a delay and another action already happened in progress, causing a wrong behaviour.

As you can notice in the code snippet above, if you some tracking might not be valid anymore.

 

captureList the famous solution for most of the closure problem is to have a of the objects it encloses and references it, for example

this will solve the retain cycle problem. 

 

However, this was a never-ending discussion with my former team mate for multiple months as he had this philosophy of weakfying every self to be safe before executing the block then make a strong reference to it inside the block, my issue here was that I don't believe in useless extra code, specially that many times you need to have strong references for some calls (e.g tracking or updating shared data) or a UIViewAnimationBlock. so I was against it using as a rule of thumb.

 

Avoiding memory leaks

  • Use structs and not classes unless you really need referencing, after all it avoids memory reference so less to worry about leaks.

  • Use weak when two classes reference each other to break a retain cycle.

  • When a closure is a class property, make sure to capture [weak self] or any other object which will be used inside it , if it might cause a retain cycle. However to avoid crashes from having the captured object de-allocated , make sure to guard it before using it inside the block.

  • Add to the  init() and deinit()method with a meaningful message as in the example above, as it is very useful when debugging.

  • Make it a habit to alway use the amazing VisualMemoryGraphDebugger in Xcode to detect retain cycles, before making a commit.

BONUS: Stack & Heap

Normally It shouldn't be bothered about where the memory allocated physically, however understanding it, will also be helpful when writing code to make better decisions of which data structure to use in case dealing with performance sensitive one.


so briefly speaking Stack is the segment of the memory where local variables are stored (e.g. ones used in a function) and data structures of known size (e.g. Int) at compile-time, they are fast to access and relatively

cheap compared to Heap, in swift the value types (struct and enum) are stored on a Stack.

 

Heap is the segment of the memory where dynamic allocation happens at run-time,  reference types (e.g class) are allocated and deallocated on Heap according to the referenceCount as mentioned earlier.

 

 

 

 

 

Share on Facebook
Share on Twitter
Please reload

Mindmap for Mobile Apps dev. environment

February 17, 2019

iOS SNAPSHOT Testing Usages

January 11, 2019

1/3
Please reload

This site was designed with the
.com
website builder. Create your website today.
Start Now