Swift - ARC Overview



Automatic Reference Counting in Swift

Automatic Reference Counting(ARC) is a memory management mechanism used by Swift, it automatically tracks and manages the allocation and deallocation of objects’s memory. Or we can say that it is used to initialize and deinitialize the system resources thereby releasing memory spaces used by the class instances when the instances are no longer needed. It keeps track of information about the relationships between the instances to manage the memory resources effectively. ARC is generally applicable only to instances of classes because they are reference types.

Working of ARC

  • ARC allocates a chunk of memory to store the information whenever a new class instance is created by init().

  • Information about the instance type and its values are stored in the memory.

  • When the class instance is no longer needed it automatically frees the memory space by deinit() for further class instance storage and retrieval.

  • ARC keeps in track of currently referring class instances properties, constants and variables so that deinit() is applied only to those unused instances.

  • ARC maintains a 'strong reference' to those class instance properties, constants and variables to restrict deallocation when the class instance is currently in use.

Example

Swift program to demonstrate ARC.

class StudDetails {

   // Properties
   var stname: String
   var mark: Int
    
   // Initializer to initialize instances of the class
   init(stname: String, mark: Int) {
      self.stname = stname
      self.mark = mark
   }
    
   // Deinitializer to deinitialize the instances of 
   // the class when they are no longer required 
   deinit {
      print("Deinitialized \(self.stname)")
      print("Deinitialized \(self.mark)")
   }
}

// Creating class instance
var obj1 = StudDetails( stname:"Swift", mark:98)

// Creating another class instance
var obj2: StudDetails? = StudDetails(stname: "iOS", mark: 95)

// Setting anotherObj to nil to deallocate the memory
obj2 = nil

Output

It will produce the following output −

Deinitialized iOS
Deinitialized 95

Strong Reference Cycles Between Class Instances in Swift

ARC is used to automatically manage memory by keeping track of object references. However, there is a challenge faced by the developers which is the creation of a strong reference cycle, where two or more objects have strong references to each other which prevents their reference counts from reaching zero and due to this a memory leakage happens.

Example

Swift program to demonstrate strong reference cycles between class instances in Swift.

class studmarks {
   let name: String
   var stud: student?
    
   // Initializer
   init (name: String) {
      print("Initializing: \(name)")
      self.name = name
   }
    
   // Deinitializer
   deinit {
      print("Deallocating: \(self.name)")
   }
}

class student {
   let name: String
   var strname: studmarks?
    
   // Initializer
   init (name: String) {
      print("Initializing: \(name)")
      self.name = name
   }
    
   // Deinitializer
   deinit {
      print("Deallocating: \(self.name)")
   }
}

// Declare optional variables for instances
var shiba: studmarks?
var mari: student?

shiba = studmarks(name: "Swift 4")
mari = student(name: "ARC")

// Create a strong reference cycle by assigning references to each other
shiba!.stud = mari
mari!.strname = shiba

Output

It will produce the following output −

Initializing: Swift 4
Initializing: ARC

To resolve strong reference cycles Swift provides Weak and Unowned References. These references are used to enable one instance to refer to other instances in a reference cycle. Then the instances may refer to every instance instead of caring about a strong reference cycle.

Weak References

It is an optional reference to an object that does not keep a strong hold on the object. It will return nil when the object it refers to is deallocated. It is generally used where the referenced object’s lifecycle is not guaranteed to outlive the referencing object. It is commonly used for relationships where the referenced object can deallocate independently. We can declare weak references using the weak Keyword.

Syntax

Following is the syntax for weak reference −

class Class1 {
   var name: Name?
}

class Name {
   weak var teacher: Class1?
}

Example

Swift program to demonstrate weak references.

class module {
   let name: String
   init(name: String) { self.name = name }
   var sub: submodule?
   deinit { print("\(name) Is The Main Module") }
}

class submodule {
   let number: Int
    
   init(number: Int) { self.number = number }
    
   weak var topic: module?
    
   deinit { print("Sub Module with its topic number is \(number)") }
}

var toc: module?
var list: submodule?
toc = module(name: "ARC")
list = submodule(number: 4)
toc!.sub = list
list!.topic = toc

toc = nil
list = nil
Output

It will produce the following output −

ARC Is The Main Module
Sub Module with its topic number is 4

Unowned References

It is a non-optional reference to an object that does not keep a strong hold on the object as compared to a weak reference. It assumed that the reference object will never become nil, while the reference object is still alive. It is generally used where you know that the referenced object’s life is guaranteed to be at least as long as the referencing object’s lifetime. It is commonly used for relationships where the referenced object has the same or longer lifetime as compared to the referencing object. We can declare unowned references using the unowned Keyword.

Syntax

Following is the syntax for unowned reference −

class Class1 {
   var name: Name?
}
class Name {
   unowned var teacher: Class1
}

Example

Swift program to demonstrate Unowned references.

class student {
   let name: String
   var section: marks?
    
   init(name: String) {
      self.name = name
   }
    
   deinit { print("\(name)") }
}
class marks {
   let marks: Int
   unowned let stname: student
    
   init(marks: Int, stname: student) {
      self.marks = marks
      self.stname = stname
   }
    
   deinit { print("Marks Obtained by the student is \(marks)") }
}

var module: student?
module = student(name: "ARC")
module!.section = marks(marks: 98, stname: module!)
module = nil
Output

It will produce the following output −

ARC
Marks Obtained by the student is 98

Strong Reference Cycles for Closures

When we assign a closure to the class instance property and to the body of the closure to capture particular instance strong reference cycle can occur. Strong reference to the closure is defined by 'self.someProperty' or 'self.someMethod()'. Strong reference cycles are used as reference types for the closures.

Example

Swift program to demonstrate Strong Reference Cycles for Closures.

class HTMLElement {
   let samplename: String
   let text: String?
    
   lazy var asHTML: () -> String = {
      if let text = self.text {
         return "<\(self.samplename)>\(text)</\(self.samplename)>"
      } else {
         return "<\(self.samplename) />"
      }
   }
    
   init(samplename: String, text: String? = nil) {
      self.samplename = samplename
      self.text = text
   }
    
   deinit {
      print("\(samplename) is being deinitialized")
   }
}

var paragraph: HTMLElement? = HTMLElement(samplename: "p", text: "Welcome to Closure SRC")
print(paragraph!.asHTML())

Output

It will produce the following output −

<p>Welcome to Closure SRC</p>

Resolving Strong Reference Cycles for Closures

We can resolve the strong reference cycles for closures using Weak and Unowned References. When the closure and the instance refer to each other the user may define the capture in a closure as an unowned reference. Then it would not allow the user to deallocate the instance at the same time. When the instance sometimes returns a 'nil' value define that closure with the weak instance.

Example

class HTMLElement {
   let module: String
   let text: String?
    
   lazy var asHTML: () -> String = {
      [unowned self] in
      if let text = self.text {
         return "<\(self.module)>\(text)</\(self.module)>"
      } else {
         return "<\(self.module) />"
      }
   }
    
   init(module: String, text: String? = nil) {
      self.module = module
      self.text = text
   }
   
   deinit {
      print("\(module) the deinit()")
   }
}

var paragraph: HTMLElement? = HTMLElement(module: "Inside", text: "ARC Weak References")
print(paragraph!.asHTML())
paragraph = nil

Output

It will produce the following output −

<Inside>ARC Weak References</Inside>
Inside the deinit()
Advertisements