懒惰的 Var 与 Let

Lazy Var vs Let

我想对 Swift 中的某些属性使用延迟初始化。 我当前的代码如下所示:

lazy var fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

事实是,一旦设置了字体大小,它就永远不会改变。 所以我想做这样的事情:

lazy let fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

这是不可能的。

只有这个有效:

let fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

所以 - 我想要一个 属性 将被延迟加载但永远不会改变。 正确的方法是什么?使用 let 并忘记惰性初始化?或者我应该使用 lazy var 而忘记 属性 的常数性质?

Swift 书 has the following note:

You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes. Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.

这在实现语言的上下文中是有意义的,因为所有常量存储属性都是在对象初始化完成之前计算的。这并不意味着 letlazy 一起使用时可以更改语义,但尚未完成,因此 var 仍然是与 [=12] 的唯一选项=] 此时。

至于你提出的两个选择,我会根据效率在它们之间做出决定:

  • 如果很少访问 属性 的值,并且前期计算成本很高,我会使用 var lazy
  • 如果该值在超过 20..30% 的情况下被访问或者计算成本相对较低,我会使用 let

注意:我会进一步优化您的代码以将条件推入 CGFloat 初始值设定项:

let fontSize : CGFloat = CGFloat(someCase  ? 30 : 17)

这是来自 Xcode 6.3 Beta / Swift 1.2 release notes 的最新经文:

let constants have been generalized to no longer require immediate initialization. The new rule is that a let constant must be initialized before use (like a var), and that it may only be initialized: not reassigned or mutated after initialization.

This enables patterns like:

let x: SomeThing
if condition {
    x = foo()
} else {
    x = bar()
}

use(x)

which formerly required the use of a var, even though there is no mutation taking place. (16181314)

显然你不是唯一对此感到沮丧的人。

正如 dasblinkenlight 指出的那样,在 Swift 中应始终将惰性属性声明为变量。然而,有可能使 属性 read-only 因此它只能从定义实体的源文件中进行变异。这是我能定义的最接近 "lazy let" 的地方。

private(set) lazy var fontSize: CGFloat = {
    if someCase {
        return 30
    } else {
        return 17
    }
}()

您可以将 Burritos 用于惰性常量属性。该库为 Swift 5.1 提供了不同的 属性 包装器。通过将以下行添加到 Podfile 来使用 CocoaPods 安装它:

pod 'Burritos'

用这个库你可以替换

lazy var fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

@LazyConstant var fontSize : CGFloat = {
    if (someCase) {
        return CGFloat(30)
    } else {
        return CGFloat(17)
    }
}()

然后self.fontSize = 20导致编译错误