Weak references - RaduG/swift_learning GitHub Wiki
Overview
As Swift uses ARC to clear unreachable objects, reference cycles are problematic. To solve this, it is possible to declare weak references. A weak reference must always be of an optional type as ARC will set it to nil on cleanup. Im
In this example, nothing gets printed, as person1 has a strong reference of school1 and vice-versa:
class Person {
let name: String
var school: School?
init(_ name: String) {
self.name = name
}
deinit {
print("Cleaning Person \(name)")
}
}
class School {
let name: String
var pupils = [Person?]()
init(_ name: String) {
self.name = name
}
func addPupil(_ pupil: Person?) {
pupils.append(pupil)
}
deinit {
print("Cleaning School \(name)")
}
}
var person1: Person? = Person("Radu")
var school1: School? = School("Some School")
person1!.school = school1!
school1!.addPupil(person1!)
person1 = nil
school1 = nil
However, if Person
's reference to a school is weak, then the memory can be cleaned:
class Person {
let name: String
weak var school: School?
init(_ name: String) {
self.name = name
}
deinit {
print("Cleaning Person \(name)")
}
}
class School {
let name: String
var pupils = [Person?]()
init(_ name: String) {
self.name = name
}
func addPupil(_ pupil: Person?) {
pupils.append(pupil)
}
deinit {
print("Cleaning School \(name)_")
}
}
var person1: Person? = Person("Radu")
var school1: School? = School("Some School")
person1!.school = school1!
school1!.addPupil(person1!)
person1 = nil
school1 = nil
Both deinitialisers are called.
Unowned references
Similarly to weak
, unowned
references do not keep a strong hold of the instance they refer to. However, an unowned relationship should only be used when the lifetime of the target object extends beyond the lifetime of the current one. Therefore, unowned variables are not optional types as ARC does not assign nil to them.
class Person {
let name: String
var card: Card?
init(_ name: String) {
self.name = name
}
deinit {
print("Removing \(name)")
}
}
class Card {
let number: String
let pin: String
unowned let owner: Person
init(number: String, pin: String, owner: Person) {
self.number = number
self.pin = pin
self.owner = owner
}
deinit {
print("Removing \(number)")
}
}
var person1: Person? = Person("Radu")
var card: Card? = Card(number: "55", pin: "1234", owner: person1!)
person1!.card = card
person1 = nil
card = nil
In this case, a person may have a credit card, but a credit card will always belong to a person.
Closures
In the example below, the greeting
closure holds a strong reference to self
and the instance holds a strong reference to the closure:
class Person {
let firstName: String
let lastName: String
let age: Int
lazy var greeting = {
"Hello, my name is \(self.firstName) \(self.lastName) and I am \(self.age) years old"
}
init(_ firstName: String, _ lastName: String, _ age: Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
deinit {
print("Cleaning \(firstName) \(lastName)")
}
}
var radu: Person? = Person("Radu", "Ghitescu", 100)
print(radu!.greeting())
radu = nil
To fix this, we need to declare a capture list in the closure to hold a weak reference to self
instead:
class Person {
let firstName: String
let lastName: String
let age: Int
lazy var greeting = {
[unowned self] in
"Hello, my name is \(self.firstName) \(self.lastName) and I am \(self.age) years old"
}
init(_ firstName: String, _ lastName: String, _ age: Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
deinit {
print("Cleaning \(firstName) \(lastName)")
}
}
var radu: Person? = Person("Radu", "Ghitescu", 100)
print(radu!.greeting())
radu = nil