Are lazy vars in Swift computed more than once?

2020-02-04 07:04发布

问题:

Are lazy vars in Swift computed more than once? I was under the impression that they replaced the:

if (instanceVariable) {
    return instanceVariable;
}

// set up variable that has not been initialized

Paradigm from Objective-C (lazy instantiation).

Is that what they do? Basically only called once the first time the app asks for the variable, then just returns what was calculated?

Or does it get called each time like a normal computed property?

The reason I ask is because I basically want a computed property in Swift that can access other instance variables. Say I have a variable called "fullName" and it just concatenates firstName and lastName. How would I do that in Swift? It seems like lazy vars are the only way to go, as in normal computed vars (non-lazy) I can't access other instance variables.

So basically:

Do lazy vars in Swift get called more than once? If so, how do I create a computed variable that can access instance variables? If not, if I only want a variable to be computed once for performance reasons, how do I do this?

回答1:

lazy vars are only calculated once, the first time you use them. After that, they're just like a normal variable.

This is easy to test in a playground:

class LazyExample {
    var firstName = "John"
    var lastName = "Smith"
    lazy var lazyFullName : String = {
        [unowned self] in
        return "\(self.firstName) \(self.lastName)"
    }()
}

let lazyInstance = LazyExample()

println(lazyInstance.lazyFullName)
// John Smith

lazyInstance.firstName = "Jane"

println(lazyInstance.lazyFullName)
// John Smith

lazyInstance.lazyFullName = "???"

println(lazyInstance.lazyFullName)
// ???

If you'll want to recalculate it later, use a computed property (with a backing variable, if it's expensive) - just like you did in Objective-C.



回答2:

No, lazy properties are initialized only once. If you set a new value, or reset to nil (for optional properties), the lazy initializer is not invoked again.

I think what you need is a computed property - it's not backed by a stored property, so it is not involved in the initialization, and as such you can refer other instance properties.

Why do you say that "normal computed vars (non-lazy) I can't access other instance variables"?



回答3:

All the other answers are correct, I would just like to add that Apple warns about lazy variables and concurrency:

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.



回答4:

Answers stating that a lazy var can only be computed once are not true. From the documentation at https://docs.swift.org/swift-book/LanguageGuide/Properties.html, the following is stated:

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

Also, please watch this talk: https://developer.apple.com/videos/play/wwdc2016/720/. At around 17:00, the following screen appears:

That talk gives you more insight about multithreading, I recommend you to watch it!