Welcome to FutureAppLaboratory

v=(*^ワ^*)=v

Learning Swift Part 13 - ARC

| Comments

===== Full Notes =====

Automatic Reference Counting

  • Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you do not need to think about memory management yourself.

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

  • How ARC Works

    To make this possible, whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance. The reference is called a “strong“ reference because it keeps a firm hold on that instance, and does not allow it to be deallocated for as long as that strong reference remains.

  • ARC in Action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    class Person {
        let name: String
        init(name: String) {
            self.name = name
            println("\(name) is being initialized")
        }
        deinit {
            println("\(name) is being deinitialized")
        }
    }

    // The next code snippet defines three variables of type Person?, which are used to set up multiple references to a new Person instance in subsequent code snippets. Because these variables are of an optional type (Person?, not Person), they are automatically initialized with a value of nil, and do not currently reference a Person instance.
    var reference1: Person?
    var reference2: Person?
    var reference3: Person?

    reference2 = reference1
    reference3 = reference1

    reference1 = nil
    reference2 = nil

    reference3 = nil
    // prints "John Appleseed is being deinitialized"
  • Strong Reference Cycles Between Class Instances

    It is 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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    class Person {
        let name: String
        init(name: String) { self.name = name }
        var apartment: Apartment?
        deinit { println("\(name) is being deinitialized") }
    }

    class Apartment {
        let number: Int
        init(number: Int) { self.number = number }
        var tenant: Person?
        deinit { println("Apartment #\(number) is being deinitialized") }
    }

    var john: Person?
    var number73: Apartment?

    john = Person(name: "John Appleseed")
    number73 = Apartment(number: 73)

    john!.apartment = number73
    number73!.tenant = john
    // The strong references between the Person instance and the Apartment instance remain and cannot be broken.
  • Resolving Strong Reference Cycles Between Class Instances

    Swift provides two ways to resolve strong reference cycles when you work with properties of class type: weak references and unowned references. Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime. Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialization.

  • Weak References

    Weak references must be declared as variables, to indicate that their value can change at runtime. A weak reference cannot be declared as a constant.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    class Person {
        let name: String
        init(name: String) { self.name = name }
        var apartment: Apartment?
        deinit { println("\(name) is being deinitialized") }
    }

    class Apartment {
        let number: Int
        init(number: Int) { self.number = number }
        weak var tenant: Person?
        deinit { println("Apartment #\(number) is being deinitialized") }
    }

    var john: Person?
    var number73: Apartment?

    john = Person(name: "John Appleseed")
    number73 = Apartment(number: 73)

    john!.apartment = number73
    number73!.tenant = john

    john = nil
    // prints "John Appleseed is being deinitialized"
    // The Person instance still has a strong reference to the Apartment instance, but the Apartment instance now has a weak reference to the Person instance. This means that when you break the strong reference held by the john variables, there are no more strong references to the Person instance.
    // Because there are no more strong references to the Person instance, it is deallocated.

    number73 = nil
    // prints "Apartment #73 is being deinitialized"
    // The only remaining strong reference to the Apartment instance is from the number73 variable. If you break that strong reference, there are no more strong references to the Apartment instance.
    // Because there are no more strong references to the Apartment instance, it too is deallocated.
  • Unowned References

    Like weak references, an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is assumed to always have a value. Because of this, an unowned reference is always defined as a non-optional type. You indicate an unowned reference by placing the unowned keyword before a property or variable declaration.

    Because an unowned reference is non-optional, you don’t need to unwrap the unowned reference each time it is used. An unowned reference can always be accessed directly. However, ARC cannot set the reference to nil when the instance it refers to is deallocated, because variables of a non-optional type cannot be set to nil.

    If you try to access an unowned reference after the instance that it references is deallocated, you will trigger a runtime error. Use unowned references only when you are sure that the reference will always refer to an instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    class Customer {
        let name: String
        var card: CreditCard?
        init(name: String) {
            self.name = name
        }
        deinit { println("\(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 { println("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
    // prints "John Appleseed is being deinitialized"
    // prints "Card #1234567890123456 is being deinitialized"
  • Unowned References and Implicitly Unwrapped Optional Properties

    The Person and Apartment example shows a situation where two properties, both of which are allowed to be nil, have the potential to cause a strong reference cycle. This scenario is best resolved with a weak reference.

    The Customer and CreditCard example shows a situation where one property that is allowed to be nil and another property that cannot be nil have the potential to cause a strong reference cycle. This scenario is best resolved with an unowned reference.

    However, there is a third scenario, in which both properties should always have a value, and neither property should ever be nil once initialization is complete. In this scenario, it is useful to combine an unowned property on one class with an implicitly unwrapped optional property on the other class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    class Country {
        let name: String
        let capitalCity: City!
        init(name: String, capitalName: String) {
            self.name = name
            self.capitalCity = City(name: capitalName, country: self)
        }
    }

    class City {
        let name: String
        unowned let country: Country
        init(name: String, country: Country) {
            self.name = name
            self.country = country
        }
    }

    var country = Country(name: "Canada", capitalName: "Ottawa")
    println("\(country.name)'s capital city is called \(country.capitalCity.name)")
    // prints "Canada's capital city is called Ottawa"
  • Strong Reference Cycles for Closures

    A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance. This capture might occur because the closure’s body accesses a property of the instance, such as self.someProperty, or because the closure calls a method on the instance, such as self.someMethod(). In either case, these accesses cause the closure to “capture” self, creating a strong reference cycle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    // The example below shows how you can create a strong reference cycle when using a closure that references self. This example defines a class called HTMLElement, which provides a simple model for an individual element within an HTML document:
    class HTMLElement {

        let name: String
        let text: String?

        // The asHTML property is declared as a lazy property, because it is only needed if and when the element actually needs to be rendered as a string value for some HTML output target. The fact that asHTML is a lazy property means that you can refer to self within the default closure, because the lazy property will not be accessed until after initialization has been completed and self is known to exist.
        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 {
            println("\(name) is being deinitialized")
        }

    }

    // The paragraph variable above is defined as an optional HTMLElement, so that it can be set to nil below to demonstrate the presence of a strong reference cycle.
    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
    println(paragraph!.asHTML())
    // prints "<p>hello, world</p>"
Unfortunately, the HTMLElement class, as written above, creates a strong reference cycle between an HTMLElement instance and the closure used for its default asHTML. ( Even though the closure refers to self multiple times, it only captures one strong reference to the HTMLElement instance. )
1
2
3
    paragraph = nil
    // If you set the paragraph variable to nil and break its strong reference to the HTMLElement instance, neither the HTMLElement instance nor its closure are deallocated, because of the strong reference cycle
    // Note that the message in the HTMLElement deinitializer is not printed, which shows that the HTMLElement instance is not deallocated.
  • 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. As with strong reference cycles between two class instances, you declare each captured reference to be a weak or unowned reference rather than a strong reference. The appropriate choice of weak or unowned depends on the relationships between the different parts of your code.

    * Swift requires you to write self.someProperty or self.someMethod (rather than just someProperty or someMethod) whenever you refer to a member of self within a closure. This helps you remember that it’s possible to capture self by accident.

  • Defining a Capture List

    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 someInstance). These pairings are written within a pair of square braces, separated by commas.

1
2
3
4
5
6
7
8
9
10
11
    lazy var someClosure: (Int, String) -> String = {
        [unowned self] (index: Int, stringToProcess: String) -> String in
        // closure body goes here
    }

If a closure does not specify a parameter list or return type because they can be inferred from context, place the capture list at the very start of the closure, followed by the in keyword:

    lazy var someClosure: () -> String = {
        [unowned self] in
        // closure body goes here
    }
  • Weak and Unowned References
    • 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. This enables you to check for their existence within the closure’s body.
    • If the captured reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    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 {
            println("\(name) is being deinitialized")
        }

    }

    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
    println(paragraph!.asHTML())
    // prints "<p>hello, world</p>"

    paragraph = nil
    // prints "p is being deinitialized"

Comments