Strong reference cycles

A strong reference cycle is where the instances of two classes holds a strong reference to each other, preventing ARC from releasing either instance. Once again, we are not able to use a Playground for this example, so we need to create an Xcode project. In this project, we start off by creating two classes named MyClass1_Strong and MyClass2_Strong with the following code:

class MyClass1_Strong { 
  var name = "" 
  var class2: MyClass2_Strong? 
  init(name: String) { 
    self.name = name 
    print("Initializing class1_Strong with name (self.name)") 
  } 
  deinit { 
    print("Releasing class1_Strong with name (self.name)") 
  } 
} 

As we can see from the code, MyClass1_Strong contains an instance of MyClass2_Strong, therefore, the instance of MyClass2_Strong cannot be released until MyClass1_Strong is destroyed. We can also see from the code that MyClass2_Strong contains an instance of MyClass1_Strong, therefore, the instance of MyClass1_Strong cannot be released until MyClass2_Strong is destroyed. This creates a cycle of dependency in which neither instance can be destroyed until the other one is destroyed. Let's see how this works by running the following code:

var class1: MyClass1_Strong? = MyClass1_Strong(name: "Class1_Strong") 
var class2: MyClass2_Strong? = MyClass2_Strong(name: "Class2_Strong") 
 
class1?.class2 = class2 
class2?.class1 = class1 
 
print("Setting classes to nil") 
class2 = nil 
class1 = nil 

In this example we create instances of both the MyClass1_Strong and MyClass2_Strong classes. We then set the class2 property of the class1 instance to the MyClass2_Strong instance. We also set the class1 property of the class2 instance to the MyClass1_Strong instance. This means that the MyClass1_Strong instance cannot be destroyed until the MyClass2_Strong instance is destroyed. This means that the reference counters for each instance will never reach zero, therefore, ARC cannot destroy the instances, which creates a memory leak. A memory leak is where an application continues to use memory and does not properly release it. This can cause an application to eventually crash.

To resolve a strong reference cycle, we need to prevent one of the classes from keeping a strong hold on the instance of the other class, thereby allowing ARC to destroy them both. Swift provides two ways of doing this by letting us define the properties as either a weak or unowned reference.

The difference between a weak reference and an unowned reference is that the instance which a weak reference refers to can be nil, whereas the instance that an unowned reference is referring to cannot be nil. This means that when we use a weak reference, the property must be an optional property, since it can be nil. Let's see how we would use unowned and weak references to resolve a strong reference cycle. Let's start by looking at the unowned reference.

We begin by creating two more classes, MyClass1_Unowned and MyClass2_Unowned:

class MyClass1_Unowned { 
  var name = "" 
  unowned let class2: MyClass2_Unowned 
  init(name: String, class2: MyClass2_Unowned) { 
    self.name = name 
    self.class2 = class2 
    print("Initializing class1_Unowned with name (self.name)") 
  } 
  deinit { 
    print("Releasing class1_Unowned with name (self.name)") 
  } 
 
} 
 
class MyClass2_Unowned { 
  var name = "" 
  var class1: MyClass1_Unowned? 
  init(name: String) { 
    self.name = name 
    print("Initializing class2_Unowned with name (self.name)") 
  } 
  deinit { 
    print("Releasing class2_Unowned with name (self.name)") 
  } 
} 

The MyClass1_Unowned class looks pretty similar to classes in the preceding example. The difference here is the MyClass1_Unowned class--we set the class2 property to unowned, which means it cannot be nil and it does not keep a strong reference to the instance that it is referring to. Since the class2 property cannot be nil, we also need to set it when the class is initialized.

Let's see how we can initialize and deinitialize the instances of these classes with the following code:

let class2 = MyClass2_Unowned(name: "Class2_Unowned") 
let class1: MyClass1_Unowned? = MyClass1_Unowned(name: "class1_Unowned",
class2: class2) class2.class1 = class1 print("Classes going out of scope")

In the preceding code, we create an instance of the MyClass_Unowned class and then use that instance to create an instance of the MyClass1_Unowned class. We then set the class1 property of the MyClass2 instance to the MyClass1_Unowned instance we just created. This creates a reference cycle of dependency between the two classes again, but this time, the MyClass1_Unowned instance is not keeping a strong hold on the MyClass2_Unowned instance, allowing ARC to release both instances when they are no longer needed.

If we run this code, we see the following output, showing that both the MyClass3 and MyClass4 instances are released and the memory is freed:

Initializing class2_Unowned with name Class2_Unowned
Initializing class1_Unowned with name class1_Unowned
Classes going out of scope
Releasing class2_Unowned with name Class2_Unowned
Releasing class1_Unowned with name class1_Unowned

As we can see, both instances are properly released. Now let's look at how we would use a weak reference to prevent a strong reference cycle. Once again we begin by creating two new classes:

class MyClass1_Weak { 
  var name = "" 
  var class2: MyClass2_Weak? 
  init(name: String) { 
    self.name = name 
    print("Initializing class1_Weak with name (self.name)") 
  } 
  deinit { 
    print("Releasing class1_Weak with name (self.name)") 
  } 
} 
 
class MyClass2_Weak { 
  var name = "" 
  weak var class1: MyClass1_Weak? 
  init(name: String) { 
    self.name = name 
    print("Initializing class2_Weak with name (self.name)") 
  } 
  deinit { 
    print("Releasing class2_Weak with name (self.name)") 
  } 
} 

The MyClass1_Weak and MyClass2_Weak classes look very similar to the previous classes we created that showed how a strong reference cycle works. The difference is that we define the class1 property in the MyClass2_Weak class as a weak reference.

Now, let's see how we can initialize and deinitialize instances of these classes with the following code:

let class1: MyClass1_Weak? = MyClass1_Weak(name: "Class1_Weak") 
let class2: MyClass2_Weak? = MyClass2_Weak(name: "Class2_Weak") 
 
class1?.class2 = class2 
class2?.class1 = class1 
 
print("Classes going out of scope") 

In the preceding code, we create instances of the MyClass1_Weak and MyClass2_Weak classes and then set the properties of those classes to point to the instance of the other class. Once again, this creates a cycle of dependency, but since we set the class1 property of the MyClass2_Weak class to weak, it does not create a strong reference, allowing both instances to be released.

If we run the code, we will see the following output, showing that both the MyClass5 and MyClass6 instances are released and the memory is freed:

Initializing class1_Weak with name Class1_Weak
Initializing class2_Weak with name Class2_Weak
Classes going out of scope
Releasing class1_Weak with name Class1_Weak
Releasing class2_Weak with name Class2_Weak

It is recommended that you avoid creating circular dependencies, as shown in this section, but there are times when you may need them. For those times, remember that ARC needs some help to release them.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset