在 Swift 中实现惰性属性并将其设置为 nil

Implementing a lazy property and setting it to nil in Swift

我想实现一个惰性属性,这样它只有在第一次被访问时才会有值。但是后来我想把它设置为nil来释放内存资源。然后,当应用程序再次尝试访问它时,它将被重新创建,因此当它即将被访问时,它不应该是 nil

我翻阅了 Swift 编程书籍并阅读了惰性属性,但是那里的信息很少,没有这个用例的例子。

我看到 this answer by Rudolf Adamkovic 并实现了该格式,但该项目无法在 Xcode 6.2 beta 3 中编译:Use of undeclared type 'String'

let model = MyModelClass()
lazy var recentlyAdded: [​String] = self.recents() //error here

func recents() -> [String] {
    return self.model.recentlyAdded()
}

我也尝试过这种格式,但它也无法编译并出现相同的编译时错误。

lazy var recentlyAdded: [​String] = self.model.recentlyAdded()

实现这种惰性属性的正确方法是什么?

是这样的吗?

struct S {
    var _actualVar: String? = nil
    var lazyVar: String? {
        mutating get {
            if _actualVar == nil {
                _actualVar = someCalc()
            }
            return _actualVar
        }
        set(newVar) {
            _actualVar = newVar
        }
    }
}
var s = S()
s.lazyVar  // someCalc will be called
s.lazyVar  // but not here
s.lazyVar = nil
s.lazyVar  // but it'll be called again here

请注意,与惰性 属性 一样,这确实需要使用 lazyVarget 版本,您的 s 变量仍然必须声明为var 这可能有点令人惊讶。

我无法弄清楚这个问题。不过我解决了。

当我复制并粘贴你的代码时,我得到了同样的错误:

Use of undeclared type 'String'

我删除了它并重新输入了它(没有自动完成)并且它对我有用。

看起来像是 XCode 中的错误。

我找到了解决办法。您不能像在 Objective-C 中那样使用 Swift 中的 lazy 属性 来执行惰性实例化,至少不能使用 Swift 1-3。如果你尝试,它不会被实例化,直到它被第一次访问,但如果你稍后将它设置为nil,当你再次访问它时它仍然是nil。它只会延迟实例化一次,这不是我想要的。

要实现 'true' 惰性实例化,您需要计算 属性 以及常规 属性,两者都是 var。我将常规 属性 称为 "backing" 属性。本质上,如果支持 属性 是 nil,则检查计算的 属性,如果是,则创建它,然后 return 支持 属性。这是一个计算的 属性,因此它不会在内存中存储值,而后备 属性 会。当你想得到它的值时,访问计算的属性。当您希望将其值设置为 nil 时,请设置支持 属性.

Apple 在他们的核心数据模板中使用了这种技术。他们对两者使用相同的 属性 名称,但在支持 属性 之前放置了一个下划线,它模仿了来自 Objective-C.

的 iVar

像这样的东西就可以了:

var fetchedResultsController: NSFetchedResultsController {
    get {
        if _fetchedResultsController != nil {
            return _fetchedResultsController!
        }
        _fetchedResultsController = NSFetchedResultsController(fetchRequest: ...)
        return _fetchedResultsController!
    }
    set {
        _fetchedResultsController = newValue
    }
}
var _fetchedResultsController: NSFetchedResultsController? = nil

使用方法:

_fetchResultsController = nil
self.fetchResultsController.performFetch()

我只使用可选的符号 - 即一个问号来表示它可以为零。例如

var myVar:String?