automatic referencing counting
Automatic Reference Counting (ARC) is the memory management strategy used by Swift to track and manage your app’s memory usage. ARC automatically keeps track of the number of references to each instance of a class and deallocates memory for instances that are no longer needed. While ARC is automatic, you still need to ensure that you don't create strong reference cycles, as this can lead to memory leaks.
Basic Operation
Every time you create a new instance of a class, ARC allocates a chunk of memory to store information about that instance. This memory holds information about the instance's type, its properties, and other information.
When an instance is no longer needed, ARC frees up the memory so that it can be used for other purposes. To make sure that an instance is not deallocated while it is still needed, ARC tracks the number of active references to each instance.
For example:
class Dog {
var name: String
init(name: String) {
self.name = name
print("\(name) is being initialized.")
}
deinit {
print("\(name) is being deinitialized.")
}
}
var myDog: Dog? = Dog(name: "Fido") // "Fido is being initialized" is printed
myDog = nil // "Fido is being deinitialized" is printed
Strong Reference Cycles
A strong reference cycle occurs when two class instances hold a strong reference to each other, causing a memory leak because neither instance can be deallocated.
To avoid strong reference cycles, you can use weak or unowned references:
Weak References: A weak reference doesn't keep a strong hold on the instance it refers to, and it doesn't prevent ARC from disposing of the referenced instance. It is always declared as an optional type.
Unowned References: Like a weak reference, an unowned reference doesn’t keep a strong hold on the instance it refers to. Unlike a weak reference, an unowned reference is used when the other instance has the same or longer lifetime. It is not an optional and must always have a value.
Here is an example demonstrating weak and unowned to avoid a strong reference cycle:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var bob: Person? = Person(name: "Bob")
var apt: Apartment? = Apartment(unit: "1A")
bob?.apartment = apt
apt?.tenant = bob
bob = nil
apt = nil
Strong Reference Cycles for Closures
Closures can capture and store references to variables and constants from the surrounding context in which they are defined. If a closure captures a class instance, it creates a strong reference to that instance, leading to the possibility of a strong reference cycle.
You can use a capture list to break strong reference cycles involving closures:
class SomeClass {
var name: String = "Hello"
lazy var closure: () -> String = { [weak self] in
return self?.name ?? "default"
}
}
Here, [weak self] tells Swift to capture self as a weak reference within the closure.
Summary
ARC works well to manage memory in most situations without requiring explicit cleanup code. However, it's essential to understand how to break strong reference cycles between class instances or between an instance and a closure to prevent memory leaks.