Strong Cycle /Retain Cycle in Swift

Vinodh Swamy
10 min readSep 14, 2017

--

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,

Employee class

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

Employee and MacBook Definition

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.

Retain Cycle Issue

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 property macBook which is pointing to mac
  • mac object has a property assinee with a strong reference pointing employee

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.

--

--

Vinodh Swamy
Vinodh Swamy

Responses (18)