How is automatic memory management (ARC) implemented in Swift?
sobes.tech AI
Answer from AI
ARC (Automatic Reference Counting) automatically tracks and manages references to class instances. It frees the memory occupied by an instance when there are no more strong references to it. ARC works only with class instances, not with structures or enumerations, as they are value types.
Three types of relationships between instances:
- Strong: A relationship that increases the reference count of an instance. By default, all references are strong.
- Weak: A relationship that does not increase the reference count. Used to prevent strong reference cycles.
weakreferences are always optional because the instance can be deallocated at any moment. - Unowned: A relationship that does not increase the reference count and is used when it is guaranteed that the related instance will exist longer than the current one. Not optional, but accessing a deallocated instance causes a runtime error.
Strong reference cycles occur when two or more class instances hold strong references to each other, preventing their deallocation by ARC. To resolve cycles, weak or unowned references are used.
Example of a strong reference cycle:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit { // called upon deallocation
print("\(name) is being deinitialized")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // called upon deallocation
print("Apartment \(unit) is being deinitialized")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil // Nothing prints because of the strong reference cycle
Resolving the strong reference cycle with weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Weak reference to Person
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartment \(unit) is being deinitialized")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Deallocates Person
unit4A = nil // Deallocates Apartment
Resolving the strong reference cycle with unowned:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Unowned reference to Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Card #\(number) is being deinitialized")
}
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // Deallocates Customer and CreditCard
ARC in closures: Closures can also create strong reference cycles by capturing class instances. To prevent this, capture lists like [weak self] or [unowned self] are used.
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { // lazy to allow self to be used before initialization
[unowned self] in // Capture list with "unowned self"
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // Deallocates HTMLElement