通过将property设置为nil,再次在Swift中触发延迟初始化程序

时间:2014-09-10 07:54:05

标签: ios iphone ipad cocoa-touch swift

我想要一个lazily-initialized属性,如果我将属性设置为nil,我可以再次调用它。

如果我以这种方式定义我的属性:

lazy var object = { /*init code*/ }()

...然后调用属性,初始化程序被触发一次。但是,如果我稍后在程序中将object设置为nil,则不会再次调用初始值设定项。我怎么能在Swift中做到这一点?

我查看了计算属性,但它们实际上并不存储值,因此每当我调用变量时,总会发生计算或初始化。我只想在属性为零时计算。

2 个答案:

答案 0 :(得分:16)

lazy属性初始化程序负责在读取模式下第一次访问属性时初始化该属性。设置为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
        }
    }
}
  • 初始化属性在需要初始化时返回变量的计算值,这是上例中的5整数
  • myProp是一个可选的整数(为了能够存储nil):
    • on set,它会将新值存储在_myProp属性
    • on get,如果_myPropnil,则会调用初始值设定项,将其分配给_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访问实际属性。

答案 1 :(得分: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设为零呢?在这个例子中,您可能正在观察当天的变化,因为todayPredicate必须始终代表今天。在您的观察者代码中,您只需执行此操作,例如......

self.todayPredicate = nil
self.loadEvents()
相关问题