DEV Community

Naveen Ragul B
Naveen Ragul B

Posted on

Swift - ARC

Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. ARC automatically frees up the memory used by class instances when those instances are no longer needed.

Reference counting applies only to instances of classes. Structures and enumerations are value types, not reference types, and aren’t stored and passed by reference.

  • whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance.

  • ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.

example :

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var reference1, reference2, reference3: Person?

reference1 = Person(name: "John Appleseed") // John Appleseed is being initialized
reference2 = reference1
reference3 = reference1

reference1 = nil
reference2 = nil
reference3 = nil // John Appleseed is being deinitialized
Enter fullscreen mode Exit fullscreen mode

Strong Reference Cycles Between Class Instances

  • ARC is able to track the number of references to a class instance.

  • However, it’s possible to write code in which an instance of a class never gets to a point where it has zero strong references. This can happen if two class instances hold a strong reference to each other, such that each instance keeps the other alive. This is known as a strong reference cycle.

  • Strong reference cycles can be resolved by defining some of the relationships between classes as weak or unowned references instead of as strong references.


Strong reference cycle between class instance - example 1 :

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 }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

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

john = nil
unit4A = nil
Enter fullscreen mode Exit fullscreen mode

Resolving Strong Reference Cycles Between Class Instances

Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a strong hold on it. The instances can then refer to each other without creating a strong reference cycle.

  1. Use a weak reference when the other instance has a shorter lifetime.
  2. Use an unowned reference when the other instance has the same lifetime or a longer lifetime.

Weak References

  • A weak reference is a reference that doesn’t keep a strong hold on the instance it refers to, and so doesn’t stop ARC from disposing of the referenced instance.

  • weak reference can be created by placing the weak keyword before a property or variable declaration.

  • ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated. Therefore the weak reference should be optional variable.

Property observers aren’t called when ARC sets a weak reference to nil.

example :

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") }
}
Enter fullscreen mode Exit fullscreen mode
var john: Person?
var unit4A: Apartment?

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

john!.apartment = unit4A
unit4A!.tenant = john

john = nil // Prints "John Appleseed is being deinitialized"
unit4A = nil // Prints "Apartment 4A is being deinitialized"
Enter fullscreen mode Exit fullscreen mode

Unowned References

  • An unowned reference doesn’t keep a strong hold on the instance it refers to.

  • An unowned reference is used when the other instance has the same lifetime or a longer lifetime. You indicate an unowned reference by placing the unowned keyword before a property or variable declaration.

  • an unowned reference is expected to always have a value. As a result, marking a value as unowned doesn’t make it optional, and ARC never sets an unowned reference’s value to nil.

example :

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
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}
Enter fullscreen mode Exit fullscreen mode
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

john = nil

// Prints "John Appleseed is being deinitialized"
// Prints "Card #1234567890123456 is being deinitialized"
Enter fullscreen mode Exit fullscreen mode

Strong Reference Cycles for Closures

  • Closures and Functions are reference type in swift.
  • A strong reference cycle can also occur a closure is assigned to a property of a class instance, and the body of that closure captures the instance.

Strong Reference Cycles for Closures - example :

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        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")
    }

}
Enter fullscreen mode Exit fullscreen mode
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML()) // "<p>hello, world</p>"

paragraph = nil. //neither the HTMLElement instance nor its closure are deallocated, because of the strong reference cycle.
Enter fullscreen mode Exit fullscreen mode

Resolving Strong Reference Cycles for Closures

  • You resolve a strong reference cycle between a closure and a class instance by defining a capture list as part of the closure’s definition.

  • A capture list defines the rules to use when capturing one or more reference types within the closure’s body.

  • Each item in a capture list is a pairing of the weak or unowned keyword with a reference to a class instance (such as self) or a variable initialized with some value.

example :

lazy var someClosure = {
    [unowned self, weak delegate = self.delegate]
    (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}
Enter fullscreen mode Exit fullscreen mode
  • Define a capture in a closure as an unowned reference when the closure and the instance it captures will always refer to each other, and will always be deallocated at the same time.
  • Conversely, define a capture as a weak reference when the captured reference may become nil at some point in the future. Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated.

If the captured reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference.

example :

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        [unowned self] in
        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")
    }

}
Enter fullscreen mode Exit fullscreen mode
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil  // Prints "p is being deinitialized"
Enter fullscreen mode Exit fullscreen mode

Top comments (0)