惰性初始化听起来很好用。为什么不总是默认使用惰性初始化?
Lazy Initialization sounds and works great. Why not always default to using lazy initialization?
看来惰性初始化很棒。我理解这个概念并测试了我的代码,我没有看到任何滞后。那么问题来了,为什么不总是使用惰性初始化呢?这有什么缺点?我没有意识到什么?
考虑 lazy var
的这个例子:
struct S {
lazy var lazyVar: Int = { /* compute some value */ 0 }()
}
幕后真正发生的事情是这样的:
struct S {
var _lazyVar: Int? = nil
var lazyVar: Int {
mutating get {
if let existingValue = _lazyVar {
return existingValue
}
else {
let newlyComputedValue = /* compute some value */ 0
_lazyVar = newlyComputedValue
return newlyComputedValue
}
}
}
}
如您所见,lazyVar
的每次访问都需要一个分支,以检查是否已经有一个值,或者是否有必要第一次计算一个值。这增加了开销,很容易超过通过简单(快速)推导延迟评估值的好处。
lazy
的最大缺点之一是 lazy
变量不能是常量。这个缺点最终会消除很多你必须将某些变量声明为 let
的情况。
在可读性方面,当其他开发人员阅读您的代码并看到 lazy
实现时,作为开发人员,我会立即知道这个变量是 运行 密集型的,或者这个变量在计算之前需要来自另一个来源的一些值。
看来惰性初始化很棒。我理解这个概念并测试了我的代码,我没有看到任何滞后。那么问题来了,为什么不总是使用惰性初始化呢?这有什么缺点?我没有意识到什么?
考虑 lazy var
的这个例子:
struct S {
lazy var lazyVar: Int = { /* compute some value */ 0 }()
}
幕后真正发生的事情是这样的:
struct S {
var _lazyVar: Int? = nil
var lazyVar: Int {
mutating get {
if let existingValue = _lazyVar {
return existingValue
}
else {
let newlyComputedValue = /* compute some value */ 0
_lazyVar = newlyComputedValue
return newlyComputedValue
}
}
}
}
如您所见,lazyVar
的每次访问都需要一个分支,以检查是否已经有一个值,或者是否有必要第一次计算一个值。这增加了开销,很容易超过通过简单(快速)推导延迟评估值的好处。
lazy
的最大缺点之一是 lazy
变量不能是常量。这个缺点最终会消除很多你必须将某些变量声明为 let
的情况。
在可读性方面,当其他开发人员阅读您的代码并看到 lazy
实现时,作为开发人员,我会立即知道这个变量是 运行 密集型的,或者这个变量在计算之前需要来自另一个来源的一些值。