Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2021 Swift

WWDC21 · 21 min · Swift

ARC in Swift: Basics and beyond

Learn about the basics of object lifetimes and ARC in Swift. Dive deep into what language features make object lifetimes observable, consequences of relying on observed object lifetimes and some safe techniques to fix them.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 13 snippets

ARC Example swift · at 1:49 ↗
class Traveler {
    var name: String
    var destination: String?
}

func test() {
    let traveler1 = Traveler(name: "Lily")
    let traveler2 = traveler1
    traveler2.destination = "Big Sur"
    print("Done traveling")
}
Reference Cycle Example swift · at 6:37 ↗
class Traveler {
    var name: String
    var account: Account?
    func printSummary() {
        if let account = account {
            print("\(name) has \(account.points) points")
        }
    }
}

class Account {
    var traveler: Traveler
    var points: Int
}

func test() {
    let traveler = Traveler(name: "Lily")
    let account = Account(traveler: traveler, points: 1000)
    traveler.account = account
    traveler.printSummary()
}
Weak Reference Example swift · at 9:05 ↗
class Traveler {
    var name: String
    var account: Account?
    func printSummary() {
        if let account = account {
            print("\(name) has \(account.points) points")
        }
    }
}

class Account {
    weak var traveler: Traveler?
    var points: Int
}

func test() {
    let traveler = Traveler(name: "Lily")
    let account = Account(traveler: traveler, points: 1000)
    traveler.account = account
    traveler.printSummary()
}
Accessing an object via weak reference swift · at 10:05 ↗
class Traveler {
    var name: String
    var account: Account?
}

class Account {
    weak var traveler: Traveler?
    var points: Int
    func printSummary() {
        print("\(traveler!.name) has \(points) points")
    }
}

func test() {
    let traveler = Traveler(name: "Lily")
    let account = Account(traveler: traveler, points: 1000)
    traveler.account = account
    account.printSummary()
}
Accessing an object via optional binding of weak reference swift · at 11:14 ↗
class Traveler {
    var name: String
    var account: Account?
}

class Account {
    weak var traveler: Traveler?
    var points: Int
    func printSummary() {
         if let traveler = traveler {
            print("\(traveler.name) has \(points) points") 
         }
    }
}

func test() {
    let traveler = Traveler(name: "Lily")
    let account = Account(traveler: traveler, points: 1000)
    traveler.account = account
    account.printSummary()
}
Safe techniques for handling weak references - withExtendedLifetime() swift · at 11:45 ↗
func test() {
    let traveler = Traveler(name: "Lily")
    let account = Account(traveler: traveler, points: 1000)
    traveler.account = account
    withExtendedLifetime(traveler) {
        account.printSummary()
    }
}

func test() {
    let traveler = Traveler(name: "Lily")
    let account = Account(traveler: traveler, points: 1000)
    traveler.account = account
    account.printSummary()
    withExtendedLifetime(traveler) {}
}

func test() {
    let traveler = Traveler(name: "Lily")
    let account = Account(traveler: traveler, points: 1000)
    defer {withExtendedLifetime(traveler) {}}
    traveler.account = account
    account.printSummary()
}
Safe techniques for handling weak references - Redesign to access via strong reference swift · at 12:55 ↗
class Traveler {
    var name: String
    var account: Account?
    func printSummary() {
        if let account = account {
            print("\(name) has \(account.points) points")
        }
    }
}

class Account {
    private weak var traveler: Traveler?
    var points: Int
}

func test() {
    let traveler = Traveler(name: "Lily")
    let account = Account(traveler: traveler, points: 1000)
    traveler.account = account
    traveler.printSummary()
}
Safe techniques for handling weak references - Redesign to avoid weak/unowned reference swift · at 14:20 ↗
class PersonalInfo {
    var name: String
}

class Traveler {
    var info: PersonalInfo
    var account: Account?
}

class Account {
    var info: PersonalInfo
    var points: Int
}
Deinitializer Example swift · at 15:23 ↗
class Traveler {
  var name: String
  var destination: String?
  deinit {
    print("\(name) is deinitializing")
  }
}

func test() {
    let traveler1 = Traveler(name: "Lily")
    let traveler2 = traveler1
    traveler2.destination = "Big Sur"
    print("Done traveling")
}
Sequencing deinitializer side-effects with external program effects swift · at 16:10 ↗
class Traveler {
    var name: String
    var id: UInt
    var destination: String?
    var travelMetrics: TravelMetrics
    // Update destination and record travelMetrics
    func updateDestination(_ destination: String) {
        self.destination = destination
        travelMetrics.destinations.append(self.destination)
    }
    // Publish computed metrics
    deinit {
        travelMetrics.publish()
    }
}

class TravelMetrics {
    let id: UInt
    var destinations = [String]()
    var category: String?
    // Finds the most interested travel category based on recorded destinations
    func computeTravelInterest()
    // Publishes id, destinations.count and travel interest category
    func publish()
}

func test() {
    let traveler = Traveler(name: "Lily", id: 1)
    let metrics = traveler.travelMetrics
    ...
    traveler.updateDestination("Big Sur")
    ...
    traveler.updateDestination("Catalina")
    metrics.computeTravelInterest()
}

verifyGlobalTravelMetrics()
Safe techniques for handing deinitalizer side effects - withExtendedLifetime() swift · at 17:56 ↗
func test() {
    let traveler = Traveler(name: "Lily", id: 1)
    let metrics = traveler.travelMetrics
    ...
    traveler.updateDestination("Big Sur")
    ...
    traveler.updateDestination("Catalina")
    withExtendedLifetime(traveler) {
        metrics.computeTravelInterest()
    }
}
Safe techniques for handing deinitalizer side effects - Redesign to limit visibility of internal class details swift · at 18:31 ↗
class Traveler {
    ...
    private var travelMetrics: TravelMetrics
    deinit {
        travelMetrics.computeTravelInterest()
        travelMetrics.publish()
    }
}

func test() {
    let traveler = Traveler(name: "Lily", id: 1)
    ...
    traveler.updateDestination("Big Sur")
    ...
    traveler.updateDestination("Catalina")
}
Safe techniques for handling deinitalizer side effects - Redesign to avoid deinitializer side-effects swift · at 19:08 ↗
class Traveler {
    ...
    private var travelMetrics: TravelMetrics
     
    func publishAllMetrics() {
        travelMetrics.computeTravelInterest()
        travelMetrics.publish()
    }

    deinit {
				assert(travelMetrics.published)
    }
}

class TravelMetrics {
    ...
    var published: Bool
    ...
}

func test() {
    let traveler = Traveler(name: "Lily", id: 1)
    defer { traveler.publishAllMetrics() }
    ...
    traveler.updateDestination("Big Sur")
    ...
    traveler.updateDestination("Catalina")
}

Resources