Site icon CodeWithSwift

Automatic Reference Counting

Swift uses Automatic Reference Counting (ARC) to manage the memory of instances of classes. ARC automatically keeps track of references to class instances and deallocates them when they are no longer needed, freeing up memory.

class Person {
    var name: String

    init(name: String) {
        self.name = name
        print("\(name) is initialized.")
    }

    deinit {
        print("\(name) is deinitialized.")
    }
}

var person1: Person? = Person(name: "Bishal")  // Reference count: 1
var person2 = person1                          // Reference count: 2

person1 = nil                                  // Reference count: 1
person2 = nil                                  // Reference count: 0
// Output: Bishal is deinitialized.

Retail Cycle and Memory Leak

A retain cycle occurs when two or more class instances hold strong references to each other, preventing ARC from deallocating them. This leads to a memory leak.

class Person {
    var name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\(name) is deinitialized.")
    }
}

class Apartment {
    var unit: String
    var tenant: Person?

    init(unit: String) {
        self.unit = unit
    }

    deinit {
        print("Apartment \(unit) is deinitialized.")
    }
}

var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")

john?.apartment = unit4A
unit4A?.tenant = john

// Breaking references
john = nil  // Retain cycle: Apartment is not deallocated.
unit4A = nil  // Retain cycle: Person is not deallocated.

Solving Retail Cycle

  1. Weak References
    • A weak reference does not increase the reference count of an instance.
    • Declared using the weak keyword.
    • Must always be an optional (nil if the instance is deallocated).
class Apartment {
    var unit: String
    weak var tenant: Person?  // Weak reference

    init(unit: String) {
        self.unit = unit
    }

    deinit {
        print("Apartment \(unit) is deinitialized.")
    }
}

2.Unowned References

An unowned reference does not increase the reference count, similar to a weak reference.Unlike weak references, it assumes the instance will always exist during its lifetime.Declared using the unowned keyword.

class Person {
    var name: String
    unowned var apartment: Apartment  // Unowned reference

    init(name: String, apartment: Apartment) {
        self.name = name
        self.apartment = apartment
    }
}

ARC and Closures

Closures can also cause retain cycles if they capture references to self or other objects.

class ViewController {
    var title: String = "MyViewController"

    lazy var printTitle: () -> Void = {
        print(self.title)
    }

    deinit {
        print("ViewController is deinitialized.")
    }
}

var vc: ViewController? = ViewController()
vc?.printTitle()
vc = nil  // Retain cycle: ViewController is not deallocated.

Using Capture Lists to Solve the Retain Cycle

Capture lists specify how references are captured within the closure.

class ViewController {
    var title: String = "MyViewController"

    lazy var printTitle: () -> Void = { [weak self] in
        guard let self = self else { return }
        print(self.title)
    }

    deinit {
        print("ViewController is deinitialized.")
    }
}

var vc: ViewController? = ViewController()
vc?.printTitle()
vc = nil  // Output: ViewController is deinitialized.

Weak vs Unowned in Swift

Weak Reference

Unowned Reference

Exit mobile version