我正在使用存储数据并对其进行一些缓存操作的结构,例如:
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, *)
}
它工作正常,除非它是可变的:
var number = Number(value: 9)
number.square // 81
number.squareRoot // 3
number.factorial // 362 880
否则,如果它是恒定的,您会收到以下错误:
let number = Number(value: 9)
number.square // Cannot use mutating getter on immutable value: 'number' is a 'let' constant
有几种流行的解决方案:
1。将 'let' 更改为 'var' 以使其可变
它仅适用于局部变量,不适用于 func 参数,因为默认情况下它们是常量,并且它强制在内部创建副本:
func f(number: Number) {
var n = number
n.factorial
}
2。将结构转换为类
这对我来说不是一个选择。
3.使用附加属性来存储缓存结果
根据这些解决方案https://stackoverflow.com/a/32292456/979986和https://oleb.net/blog/2015/12/lazy-properties-in-structs-swift/我们可以创建一个专用的类实例来缓存我们所有的计算:
例如:
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
但是这个解决方案是多余且不方便的,因为我必须克隆所有结构中的所有属性。
问题:对于大多数情况还有其他方便的方法吗?
它需要将缓存值存储在结构之外,以便为不可变实例提供通用解决方案。然后,您可以通过键从计算属性访问此存储,该键可以是此调用出现的代码位置:
class Lazy {
private static var cache = [Int : Any]()
static func `var`<T>(file: String = #file, line: Int = #line, column: Int = #column, f: () -> T) -> T {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
let key = "\(file):\(line):\(column)".hashValue
guard let value = cache[key] as? T else {
let value = f()
cache[key] = value
return value
}
return value
}
}
它不像常量实例上的惰性属性那样工作:
struct Number {
let value: Int
var square: Double { Lazy.var { pow(Double(value), 2) } }
var squareRoot: Double { Lazy.var { Double(value).squareRoot() } }
var factorial: Int { Lazy.var { (1...value).reduce(1, *) }}
}
let number = Number(value: 9)
number.square // 81
number.squareRoot // 3
number.factorial // 362 880