【问题标题】:Trigger lazy initializer again in Swift by setting property to nil通过将属性设置为 nil 在 Swift 中再次触发惰性初始化器
【发布时间】:2014-11-03 18:54:17
【问题描述】:
我想要一个延迟初始化的属性,如果我将属性设置为 nil,我可以再次调用它的初始化程序。
如果我这样定义我的财产:
lazy var object = { /*init code*/ }()
...然后调用属性,初始化器被触发一次。但是,如果我稍后在程序中将 object 设置为 nil,则不会再次调用初始化程序。我如何在 Swift 中做到这一点?
我查看了计算属性,但它们实际上并不存储值,因此每当我调用变量时,总是会发生计算或初始化。我只想在属性为零时计算。
【问题讨论】:
标签:
ios
iphone
ipad
cocoa-touch
swift
【解决方案1】:
惰性属性初始化器负责在第一次以读取模式访问属性时对其进行初始化。设置为 nil 对初始化状态没有影响 - 它只是属性存储的有效值。
您可以使用 3 个属性来模拟延迟初始化:
- 一个私有初始化器,实现为一个计算属性(如果您愿意,也可以是一个闭包)
- 一个私有的后备属性,存储实际值
- 非私有属性,即您在代码中实际使用的属性
代码如下所示:
class MyClass {
private var _myPropInitializer: Int {
return 5
}
private var _myProp: Int?
var myProp: Int? {
get {
if self._myProp == nil {
self._myProp = self._myPropInitializer
}
return _myProp!
}
set {
_myProp = newValue
}
}
}
- initializer 属性在变量需要初始化时返回一个计算值,即上面示例中的
5 整数
-
myProp 是一个可选整数(能够存储nil):
- 在设置时,它将新值存储在
_myProp 属性中
- 在获取时,如果
_myProp 是nil,它会调用初始化器,将其分配给_myProp,并返回其值
如果您想重用该模式,最好将所有内容放在一个类中:
class Lazy<T> {
private let _initializer: () -> T
private var _value: T?
var value: T? {
get {
if self._value == nil {
self._value = self._initializer()
}
return self._value
}
set {
self._value = newValue
}
}
required init(initializer: () -> T) {
self._initializer = initializer
}
}
注意:struct 不可用,因为不允许在属性 getter 中设置属性,而在类中可以。
那么你可以如下使用它:
class MyTestClass {
var lazyProp: Lazy<Int>
init() {
self.lazyProp = Lazy( { return 5 } )
}
}
操场上的一些测试:
var x = MyTestClass()
x.lazyProp.value // Prints {Some 5}
x.lazyProp.value = nil
x.lazyProp._value // Prints nil
x.lazyProp.value // Prints {Some 5}
缺点是您必须以x.lazyProp.value 而不是x.lazyProp 的身份访问实际属性。
【解决方案2】:
当您的对象只能是 nil 或计算值时,我使用了一种惰性模式。它只需要 2 个属性:
var todayPredicate: NSPredicate! {
set {
guard newValue == nil else {return} // could throw here if required
self._todayPredicateLazyBacker = nil
}
get {
guard self._todayPredicateLazyBacker == nil else {return self. _todayPredicateLazyBacker}
// construct your today predicate
let predicate = ...
self._todayPredicateLazyBacker = predicate
return self._todayPredicateLazyBacker
}
}
private var _todayPredicateLazyBacker: NSPredicate?
todayPredicate 在第一次读取时仅构造一次(惰性)。
那么你为什么要将todayPredicate 设置为nil?在此示例中,您可能正在观察一天的变化,因为todayPredicate 必须始终代表今天。例如,在您的观察者代码中,您只需执行此操作...
self.todayPredicate = nil
self.loadEvents()