【问题标题】:Is there a way to tell if a lazy var has been initialized?有没有办法判断一个惰性变量是否已被初始化?
【发布时间】:2016-11-28 15:39:28
【问题描述】:

在需要显示视图之前,我不想初始化视图控制器,所以我将它放在一个惰性变量中,例如:

lazy var foo: NSViewController! = {
    let foo = NSViewController()
    foo.representedObject = self.representedObject
    return foo
}()

// ...

override var representedObject: Any? {
    didSet {
        if foo != nil {
            foo.representedObject = representedObject
        }
    }
}

self.representedObject 是在foo 被引用之前设置的,但是每次我调用if foo != nil 时,它都会初始化foo:c

我有什么方法可以测试foo 是否已经设置了?

【问题讨论】:

    标签: swift lazy-loading lazy-initialization


    【解决方案1】:

    使用 Swift 内置惰性语义的较短版本:

    struct Foo {
        lazy var bar: Int = {
            hasBar = true
            return 123
        }()
        private(set) var hasBar = false
    }
    

    只需检查hasBar

    【讨论】:

      【解决方案2】:

      lazy 只是围绕一种特定的惰性实例化模式(并且只是适度有用的一种)的便利包装。如果您想要自己的模式,请不要使用lazy;自己构建吧。

      private var _foo: NSViewController? = nil
      var foo: NSViewController {
          if let foo = _foo {
              return foo
          }
      
          let foo = NSViewController()
          foo.representedObject = self.representedObject
          _foo = foo
          return foo
      }
      
      // This can be private or public, as you like (or you don't technically need it)
      var isFooLoaded: Bool {
          return _foo != nil
      }
      
      override var representedObject: Any? {
          didSet {
              if !isFooLoaded {
                  foo.representedObject = representedObject
              }
          }
      }
      

      这是为了遵循isViewLoaded 模式而设计的,它解决了相同的基本问题。

      【讨论】:

      • 所以没有办法判断 lazy var 之前是否被引用过?
      • 我敢肯定,如果您将自己的数据结构解压缩为不安全内存,则有一些方法。它会非常脆弱,但最终一切都只是字节,所以它必须在某个地方是可知的。但就“这是否可以比上述更安全、更简单地完成”而言?我不相信。 lazy 是一个非常简单的硬编码模式,它不会暴露其内部结构。
      • 挑战在于,让这个线程安全。 IMO,没有可靠的方法来询问“isAlreadyInitialized”,因为 - 在多线程环境中 - 结果立即过时,也就是说,它可能返回 false - 但实际上底层变量已经完成“同时初始化”时间”。
      • @CouchDeveloper 我不确定你的意思。有什么东西阻碍了标准的锁定解决方案吗?在 Swift 中有一个 OSCompareAndSwap 会很好,但即使没有它,使用 os_unfair_lock (或者在极端情况下,DispatchQueue,虽然这有点矫枉过正)使这个线程安全应该很简单。
      【解决方案3】:

      我在项目中采用的实际解决方案是使用我创建的the Lazy Containers package,其中包含一个isInitialized 字段:

      import LazyContainers
      
      
      
      @Lazy
      var foo: NSViewController = {
          let foo = NSViewController()
          foo.representedObject = self.representedObject
          return foo
      }()
      
      // ...
      
      override var representedObject: Any? {
          didSet {
              if _foo.isInitialized {
                  foo.representedObject = representedObject
              }
          }
      }
      

      【讨论】: