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 referencestrong, 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 Employee
with 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.
At line 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 MacBook
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 Employee.
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 employee
and 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 employee
and mac
employee
object has a strong reference to propertymacBook
which is pointing tomac
mac
object has a propertyassinee
with a strong reference pointingemployee
Thus they both became a retain cycle, means employee
will be retained by mac
and mac
will be retained by employee
so they both don't get freed or deallocated.
How can we resolve this? there comes the saviour weak
reference
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.
Let 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 weak
before,var assinee:Employee
and now you can see the both employee
and 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?
A 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 Employee
and 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 unowned
not 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 employee
and,icard
the 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 Employee
and 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.
Let’s create PhotosViewController
responsible for displaying the photos, and one more class PhotosViewModel
which is responsible for getting the photos to our PhotosViewController
That’s 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 PhotosViewController
through didGetPhotos
closure.
Now let’s see our PhotosViewController
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 photosViewModel
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
let’s picturise
Ohh Yea!!, seems there is retain cycle, photoViewController
has a strong reference to photosViewModel
, photosViewModel
has a strong reference to didGetPhotos()
closure, and we accessed the self inside the closure in line 106 loadPhotos()
function.
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.