Sobes.tech
Middle

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. weak references 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
How is automatic memory management (ARC) implemented… - sobes.tech