How to use lazy initialization with getter/setter method?

2.5k Views Asked by At

How i can use lazy initialization with get and set() closure.

Here is lazy initialization code:

lazy var pi: Double = {
        // Calculations...
        return resultOfCalculation
        }()

and here is getter/setter code :

var pi: Double {
    get {
        //code to execute
        return someValue
    }
    set(newValue) {
        //code to execute
    }
}
2

There are 2 best solutions below

0
On

I assume what you're trying to do is lazily generate the default for a writable property. I often find that people jump to laziness when it isn't needed. Make sure this is really worth the trouble. This would only be worth it if the default value is rarely used, but fairly expensive to create. But if that's your situation, this is one way to do it.

lazy implements one very specific and fairly limited pattern that often is not what you want. (It's not clear at all that lazy was a valuable addition to the language given how it works, and there is active work in replacing it with a much more powerful and useful system of attributes.) When lazy isn't the tool you want, you just build your own. In your example, it would look like this:

private var _pi: Double?
var pi: Double {
    get {
        if let pi = _pi { return pi }
        let result = // calculations....
        _pi = result
        return result
    }

    set { _pi = newValue }
}

This said, in most of the cases I've seen this come up, it's better to use a default value in init:

func computePi() -> Double {
    // compute and return value
}

// This is global. Globals are lazy (in a thread-safe way) automatically.
let computedPi = computePi()

struct X {
    let pi: Double // I'm assuming it was var only because it might be overridden
    init(pi: Double = computedPi) {
        self.pi = pi
    }
}

Doing it this way only computes pi once in the whole program (rather than once per instance). And it lets us make pi "write-exactly-once" rather than mutable state. (That may or may not match your needs; if it really needs to be writable, then var.)

A similar default value approach can be used for objects that are expensive to construct (rather than static things that are expensive to compute) without needing a global.

struct X {
    let pi: Double
    init(pi: ExpensiveObject = ExpensiveObject()) {
        self.pi = pi
    }
}

But sometimes getters and setters are a better fit.

0
On

The point of a lazy variable is that it is not initialized until it is fetched, thus preventing its (possibly expensive) initializer from running until and unless the value of the variable is accessed.

Well, that's exactly what a getter for a calculated variable does too! It doesn't run until and unless it is called. Therefore, a getter for a calculated variable is lazy.

The question, on the whole, is thus meaningless. (The phrase "How i can use lazy initialization" reveals the flaw, since a calculated variable is never initialized — it is calculated!)