Strong Cycle /Retain Cycle in Swift
Before Jumping into the Strong/Retain Cycle problem let’s have very brief understanding how our object memory is managed with ARC.
ARC is a compile time feature XCode provides us for automated memory management. It stands for Automatic Reference Counting. In simple terms, ARC frees objects only if the strong reference count goes to ‘zero’
According to ARC Memory management, an object can be one of reference
strong, weak or unowned type with a variable, will be discussing more about them further, for now, if the object doesn't have any key word before let statement then by default it will be with strong reference.
let name = String() // name variable has a strong reference to instance of String
Lets understand with code,
The above snippet declares a class called
name, empid & title as the ivars, At line number
19 we create a instance
jack of class
Employee passing all the employee details, so
jack variable has a strong reference to object.
According to ARC memory management object with at least one strong reference will not be free/deallocated.
20 we nil the reference to the object
jack which in turn deallocates or frees the object which it was referring to, thats how the
deinit() funtion of class
Employee is called before it frees the object, and as we see the log
Employee: Jack removed.
Let’s add the reference to the variable
jack before niling it out.
"Where did the deinit log “Employee: Jack removed” go ?" we made
jack = nil right? why did’nt it logged ? Now thats ARC memory management, the
jack object was not deallocated even though we made it nil, because the variable
jackReference at line number
20 has a strong reference to an instance were
jack was pointing to, so the strong reference count of object were
jack was pointing too was not zero hence not deallocated or freed, if we make
jackReference = nil then we will see the log of deinit
Employee: Jack removed.
That’s the verify brief ARC intro.
Now coming to actual problem, Strong/Retain Cycle
As we know each time we declare a class property by default it has a strong reference to object which it is pointing to unless we specify something, so when can a strong cycle appear?
Cases were ObjectA has a strong reference with ObjectB and ObjectB, in turn, has a strong reference to ObjectA
Let’s understand with a code as usual
Let’s make use for our
Employee class itself, and let's declare one more class
So that’s how the classes look,
Employee has 4 ivars or properties with strong reference
name, empid, title & macBook so the first 3 properties are of type String, the
macBook is of type class
MacBook, it is defined below
Employee class in the snippet, same way the
MacBook has 2 properties
serialNumber & assignee as we can see the property
assignee is of type class
Now let’s see how the strong cycle occurs
What!! where is the strong cycle? I created the instance
employee line 33, created an instance of
mac at line 34, assigned
employee?.macBook = mac then I nil both
mac you know both got removed as I can see in the log, so were is the strong cycle?
Actually, the strong cycle is not created yet, in
employee we made the property
macBook to point to an instance
mac which is a strong reference relation with
mac, but as we can see in the
MacBook class there is property called
assinee which we didn't set to
employee yet, let's add
mac.assinee = employee before we nil both
employee and mac.
Why didn’t the log print now?? we made both
employee and mac nil, so? so here we made a crime of making strong reference cycle between
employeeobject has a strong reference to property
macBookwhich is pointing to
macobject has a property
assineewith a strong reference pointing
Thus they both became a retain cycle, means
employee will be retained by
mac will be retained by
employee so they both don't get freed or deallocated.
How can we resolve this? there comes the saviour
A weak reference is a reference that does not keep a strong hold on the instance it refers to and so does not stop ARC from deallocating the referenced instance. This behaviour prevents the reference from becoming part of a strong reference cycle. You indicate a weak reference by placing the
weak keyword before a property or variable declaration.
That’s what apple document speaks about weak reference, let’s try breaking the above retain cycle with the weak keyword.
employee still have a strong reference with
macBook we will make the
assinee property of
MacBook weak by just adding the keyword
weak before the var as shown below.
Did you notice anything? yea there is a key word
var assinee:Employee and now you can see the both
macbook object/instance is deallocated as confirmed in the console
Employee : Jack removed, & Mackbook : JHSF8S32 removed is logged.
So we did break the retain cycle with the
weak reference type.
So what about
unowned ? when is that used?
unowned reference does not keep a strong hold on the instance it refers to.
That statement seems repeating? isn’t it the same definition even
weak reference we read? Yes!! Like weak reference when a variable or property declared with
unowned key word before, doesn't hold the strong reference to the object which it is pointing to.
So you mean to tell, we can break the above
MacBook retain cycle with unowned reference as well? Yes!!
Then why do we need the
unowned keyword ? there should be some condition were we should use
weak and vise versa, lets see what apple doc tells about it.
“Like a weak reference, an unowned reference does not keep a strong hold on the instance it refers to. however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime.”
Lets tweak the above statement little for our understanding
“weak reference is used where there is possibility for that reference to become nil at some point during its lifetime. an unowned reference is used where there is no possibility for that reference becoming nil at any point until the self-object exist”
Seems still confused, if it is let’s resolve that reading further with code example.
You indicate unowned reference by placing the
unowned keyword before a property or variable declaration.
Let’s use our previous
Employee class and one more class say,
ICard each an every employee will have one iCard and each iCard would be pointing to one employee
Now that’s our employee class, which has 3 properties
name, title & iCard were
iCard is of class type
ICard which is defined as shown below
As expected as we know there is retain cycle between
iCard variable of Employee class has strong reference to ICard and same way
employee variable of ICard has strong reference to Employee
We know the trick to break the retain cycle lets go and add
weak keyword before the employee variable of ICard class
Ohh!! no seems my compiler is not happy about it, it says
‘weak’ variable should have optional type ‘Employee?’ Ohh that's a good suggestion but I cannot make my employee variable in ICard class as
optional by adding ?, because the ICard class is valid only if it has a valid employee, every instance of ICard should have a valid employee associated with it, so making it optional is not the solution.
Ok, we let’s use
unowned key , as we read earlier this also used to break the retain cycle.
Ok we lets use
unowned key , as we read earlier this also used to break the retain cycle.
Wow my compiler was happy, it didn’t shout at me this time, and see line 71 I created an instance of Employee
jack , line 73 I create an instance of ICard passing the
jack as an argument, line 74 I assigned
jack.iCard = iCard, and I nill both the objects, with out any surprise or retain cycle the Employee and ICard has been removed as we can see from the logs
Got little when to use unowned ? or weak?
Ok back to our previous example
MacBook , why didn't we use
assinee as unowned, in
code : Employe and MacBook Definition because in that use case there is a chance the assignee might become nil when
jack takes new laptop, in that case, the assignee variable of MacBook would be nil until it points to the new employee it is assigned to.
So that’s not the only retain/strong cycle problem, it just doesn’t occur between class instances, there are chances of creating a retain cycle while using closures, If not sure about what is closures I recommend read closure before continuing further.
PhotosViewController responsible for displaying the photos, and one more class
PhotosViewModel which is responsible for getting the photos to our
PhotosViewModel at the top we have declared a closure type
DidGetPhotos which takes one parameter
photos which is array, As mentioned earlier this class is responsible for getting the photos to the
Now let’s see our
Let’s read the PhotsViewController line by line
line 102 we create declare and initialize a
photosViewModel object of type
PhotosViewModel, so thats the object responsible for getting photos
line 104 we defined a
loadPhotos() function in which we assign a
didGetPhotos closure to our
photosViewModel, so this is the place we get a callback from our ViewModel getting all the photos. and inside the closure we call the function
updatePhotosInView() to display the photos given by
Does this have a Retain cycle? let’s check it out, as usual, let’s create an instance of
PhotosViewContoller and nil it out, verify that it logs inside deinit(), its deallocated or freed.
Where is our deinit() log? we created an instance we called loadPhotos() and we set the photosViewController to nil, ideally, it should have deallocated right? before finding an answer let’s read below fact about closure
In swift accessing a self inside a closure makes the closure have strong reference to self.
Did it mean closure will retain self if it is used inside closure ?? Yes
Ohh Yea!!, seems there is retain cycle,
photoViewController has a strong reference to
photosViewModel has a strong reference to
didGetPhotos() closure, and we accessed the self inside the closure in line 106
Let’s break it with a trick we learned before (weak, unowned) , so will go ahead a make the didGetPhotos property weak?, no, you cannot, the compiler will shout at me, can we do something inside the closure ??
Yes absolutely!! , we have to break the cycle from closure, as we know the closure captures reference right, so we need to tell the
didGetPhotos closure inside the
PhotoViewController to capture the self with weak reference.
let’s check the code now how it looks?
Ohh yeah there you go, seems the
photoViewContoller is deallocated now, as you can see the log
Deinited the PhotosViewController, what did we do?
Observe line number 105 we captured the self as weak reference by providing
[weak self] before the closure parameters, as you see we pass that as an array , I mean we pass those captured values in an array , means we can capture more than one reference inside the closure.
That’s all I have now about Retain/Strong Cycles, feel free to drop a comment if there is anything, hope will come next time with some new problems and techniques.