I'm working with structs that stores data and makes some cached operations on it e.g.:
struct Number {
let value: Int
lazy var square = pow(Double(value), 2)
lazy var squareRoot = Double(value).squareRoot()
lazy var factorial = (1...value).reduce(1, *)
}
It works OK unless the it's variable:
var number = Number(value: 9)
number.square // 81
number.squareRoot // 3
number.factorial // 362 880
Otherwise you get the following error if it's constant:
let number = Number(value: 9)
number.square // Cannot use mutating getter on immutable value: 'number' is a 'let' constant
There are several popular solutions:
1. Change 'let' to 'var' to make it mutable
It works with local variables only and doesn't with func parameters because they are constants by default and it forces to make a copy inside:
func f(number: Number) {
var n = number
n.factorial
}
2. Convert struct to class
It's not an option in my case.
3. Use additional properties to store cached results
According to these solutions https://stackoverflow.com/a/32292456/979986 and https://oleb.net/blog/2015/12/lazy-properties-in-structs-swift/ we can create a dedicated class instance to cache all our calculations:
For instance:
struct Number {
let value: Int
class Cache {
var square: Double?
var squareRoot: Double?
var factorial: Int?
}
private let cache = Cache()
...
var factorial: Int {
guard let factorial = cache.factorial else {
let res = (1...value).reduce(1, *)
cache.factorial = res
return res
}
return factorial
}
}
let number = Number(value: 9)
number.factorial // 362 880
But this solution is redundant and inconvenient because I have to clone all my properties in all my structs.
Question: Is there any other convenient approaches for most cases?
It needs to store cached values outside your struct to make a common solution for immutable instances. Then you can access to this storage from your computed properties by a key than can be a location of code where this call appears:
Not it works like lazy properties on constant instance: