“This is the second day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

Xcode13 added the Optimize Object Lifetimes compilation option in the Build Setting, which is disabled by default. Apple recommended Setting this option to YES to reduce the Swift Object lifecycle. So you can use memory more efficiently.

Swift, the ARC.

Before changing the compiler setting to YES, let’s take a look at ARC in Swift. There are the following:

  • The life cycle of an object starts fromThe init ()toEnd of last use.
  • At the end of the life cycle,ARCWill objectdealloc.
  • ARCthroughReference countingTo track down the subjectThe life cycle.
  • Swift, the compilerBy insertingretain/releaseOperation to control reference counting.
  • When the object’sReference countingfor0When,Swift runtimeWill objectdealloc.

And OC

Let’s look at the following code

class Traveler{
    var name: String
    var destination: String?
    init(name:String) {
        self.name = name
    }
}
func test(){
    let travel1 = Traveler(name: "LiLy")  // 1
    // Retain
    let travel2 = travel1                 // 2
    //Release                             // 3
    travek2.destination = "Big Sur"       // 4    
    //Release
    print("Done traveling")
}
Copy the code

The compiler inserts a retain operation at the start of the reference and a release operation at the last use. From this, we can analyze:

  • 1,Travler objectAt initialization,Reference countingfor1.
  • 2, intravl2referencetrvel1When theTravler objectforretainOperation, at this point,Reference countingfor2.
  • 3, inLast time using Travel1When theTravler objectforreleaseOperation. At this point,Reference countingfor1.
  • 4, inTravel2 for the last timeWhen theTravler objectforreleaseOperation, at this point,Reference countingfor0.

At this point, the Travler object’s life cycle begins with its initialization and ends with its last use.

When we run this function with optimization turned on, the result is

Traveler deinit ........
Done traveling
Copy the code

We can see that Traveler is released before print(“Done traveling”), which guarantees the shortest life of the object. This is different from C++ or OC, where objects are destroyed only after the close parenthesis is executed.

The impact it might have

Weak and undirected references

In most cases, this is fine, but if there are weak or unown references, you need to pay special attention, as shown in the following example:

class Traveler{
    var name: String
    var account: Account?
    init(name:String) {
        self.name = name
    }
}

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

    init(points: Int, traveler: Traveler?){
        self.traveler = traveler
        self.points = points
    }
    
    func printSummary(){
      if let travel = traveler {
            print("\(travel.name) has \(points) points")
        }
    }
}


func test(){    
    let travel = Traveler(name: "LiLy")  
    let account = Account(points: 1000, traveler: travel)
    travel.account = account // 2
    account.printSummary()
}
Copy the code

Travel strongly references the Account object, and Account weakly references the Travel object.

We notice that since the account reference to travel is a weak reference, in the // 2 code, at this point, the Travel object has been freed, and when the // 2 function is executed, the travel object is nil, the condition is not true, and the traveler score is not printed, a silent bug will occur.

withExtendedLifetime

WithExtendedLifetime can be used to extend the lifetime of an object and prevent potential errors.

func test(){
    let travel = Traveler(name: "LiLy")
    let account = Account(points: 1000, traveler: travel)
    travel.account = account
    withExtendedLifetime(travel, {
        account.printSummary()
    })
}
Copy the code
  • willtravelThe life cycle of theaccount.printSummary()Performed.

Or use defer, which extends to the end of the entire function.

func test(){
    let travel = Traveler(name: "LiLy")  // 1
    let account = Account(points: 1000, traveler: travel)
    defer {withExtendedLifetime(travel, {})}
    travel.account = account
    account.printSummary()
}
Copy the code

That’s not a good way to do it, it increases our maintenance costs, and it defeats the purpose of reducing the object’s life cycle.

Redesign with strong references

If you can limit access to objects to only strong references, you can prevent object lifecycle accidents. Here, the printSummary() function is moved back to the Traveler class and weak references are hidden in the Account class, The printSummary() function must now be called through Travel, and since the Account is a strong reference in Traveler, potential errors can be eliminated

class Traveler{
    var name: String
    var account: Account?
    init(name:String) {
        self.name = name
    }
    
   func printSummary(){
      if let account = account {
            print("\(name) has \( account.points) points")
        }
    }
}

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

    init(points: Int, traveler: Traveler?){
        self.traveler = traveler
        self.points = points
    }
}
func test(){
    let travel = Traveler(name: "LiLy")  
    let account = Account(points: 1000, traveler: travel)
    travel.account = account
    travel.printSummary() // 2
}
Copy the code
Redesign to avoid weak/unown references

Add a middle class, store necessary information in the middle class, break weak or UNOwn references, and use the middle class to break references between objects. After the redesign, the class declaration is as follows

The reference relationship between them is shown in the figure

deinit

After optimization is enabled, the life cycle of the object will be shortened. If the project deinit method on the object does depend on the external object, the dependent external object may have been released, causing some logic errors. That’s one thing to watch out for.

conclusion

This article describes Xcode 13’s optimization of the Swift object lifecycle, the problems that can arise when this optimization is enabled, and the corresponding suggestions based on the problems themselves.

Reference for this article:

WWDC 21: Swift ARC in Swift: Basics and beyond

If you feel that there is a harvest please follow the way to a love three even: 👍 : a praise to encourage. 🌟 : Collect articles, easy to look back! . 💬 : Comment exchange, mutual progress! .