How do I declare an array of weak references in Swift?


In Swift, you can use the weak keyword to declare an array of weak objects. In this article, we will use the weak keyword to store the weak objects or references in an array.

Weak References

Weak references are one of the solutions to the retain cycle problem in Swift. Note that a weak reference does not increment or decrement the reference count of an object. They can be deallocated even if ARC has a reference count greater than 1.

Basically, we use the weak keyword in Swift to mark a reference as weak. Also, a weak reference cannot be declared with the let keyword since it will at some point be passed to nil. This is because the object could be deallocated while the weak reference is pointing to it. There should be a weak reference in the code where you think the retain cycle might be generated.

Example 1

In following example, the Weak struct holds a weak reference to a Person instance. The value property of the struct is optional, so it will be set to nil automatically if the referenced object is deallocated. The personArray is an array of Weak<Person> instances, so it holds weak references to instances of Person.

import Foundation
// defining a class with some properties
class Person {
    
   let name: String
   let age: Int
   let country: String
    
   init(name: String, age: Int, country: String) {
      self.name = name
      self.age = age
      self.country = country
   }
}
// define a struct to hold a weak reference
struct Weak<T: AnyObject> {
   weak var value: T?
    
   init(_ value: T?) {
      self.value = value
   }
}

// declare an array of weak references to Person instances
var personArray: [Weak<Person>] = []

// create an instance of Person and add a weak reference to it to the array
let anil = Person(name: "Anil Kumar", age: 25, country: "India")
personArray.append(Weak(anil))

// displaying the details
print("Person Name: \(anil.name), age: \(anil.age) and country: \(anil.country)")

Output

Person Name: Anil Kumar, age: 25 and country: India

Example 2

The following example explains how to use weak instances to avoid creating retain cycles in your project. It also explains how to use arrays of weak references to manage objects that may be deallocated at any time.

This output confirms that the weak references to person1 and person2 are still valid after their values are updated. The PersonClassManager instance can receive updates from both Person instances because it is the delegate of both of them. Finally, the loop over the array of weak references confirms that both Person instances still exist and have not been deallocated.

import Foundation

// Define a protocol for a delegate that will be used by a Person class
protocol PersonClassDelegate: AnyObject {
   func personDidUpdateValue(_ person: Person)
}

// Define a Person class that will use a weak reference to its delegate
class Person {
   weak var delegate: PersonClassDelegate?
   var age: Int = 0 {
      didSet {
         delegate?.personDidUpdateValue(self)
      }
   }
}

// Define a struct that will hold a weak reference to an object of type T
struct Weak<T: AnyObject> {
   weak var value: T?
    
   init(_ value: T?) {
      self.value = value
   }
}

// Define a class that will manage an array of weak references to Person instances
class PersonClassManager: PersonClassDelegate {
    
   var weakArray: [Weak<Person>] = []
    
   func addPerson(_ person: Person) {
      person.delegate = self
      weakArray.append(Weak(person))
   }
    
   func personDidUpdateValue(_ person: Person) {
      print("Person class did update age to \(person.age)")
   }
}

// Create an instance of PersonClassManager and add two instances of Person to it
let manager = PersonClassManager()
let person1 = Person()
let person2 = Person()
manager.addPerson(person1)
manager.addPerson(person2)

// Update the value of person1 and person2
person1.age = 25
person2.age = 28

// Check that the weak references to person1 and person2 are still valid
for weakPerson in manager.weakArray {
   if let person = weakPerson.value {
      print("Person class with age \(person.age) still exists")
   } else {
      print("Person class has been deallocated")
   }
}

Output

Person class did update age to 25
Person class did update age to 28
Person class with age 25 still exists
Person class with age 28 still exists

Conclusion

Finally, we may utilize Swift's weak references to prevent retaining cycles in our code and handle objects that may be deallocated at any time. A Weak struct that maintains a weak reference to an object of any class that is a subclass of AnyObject can be used to specify an array of weak references.

The weak reference guarantees that objects are only retained in memory for as long as they are required. They may be deallocated when they are no longer required. This can help us avoid memory leaks and enhance our code's speed and stability.

Updated on: 24-Apr-2023

582 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements