Search code examples
iosswiftlazy-initialization

Implementing a lazy property and setting it to nil in Swift


I would like to implement a lazy property so that it will not have a value until it is accessed for the first time. But then later I want to set it to nil to free up memory resources. Then when the app tries to access it again, it will be recreated, therefore it shouldn't ever be nil when it's about to be accessed.

I looked through the Swift Programming book and read up on lazy properties, but the information there is sparse, there was no example of this use case.

I saw this answer by Rudolf Adamkovic and implemented that format, but the project won't compile in 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()
}

I also tried this format, but it too fails to compile with the same compile-time error.

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

What is the proper way to implement this type of lazy property?


Solution

  • I've found a solution. You cannot use the lazy property in Swift to perform lazy instantiation like you would in Objective-C, at least not with Swift 1-3. If you attempt to, it won't be instantiated until it's accessed for the first time, but if you set it to nil later on, when you go to access it again it will still be nil. It will only be lazily instantiated a single time, which is not what I wanted.

    To implement 'true' lazy instantiation you'll need a computed property as well as a regular property, both vars. I'll term the regular property the "backing" property. Essentially, you check in the computed property if the backing property is nil and create it if it is, then return that backing property. This is a computed property so it doesn't store a value in memory, while the backing property does. When you wish to get its value, access the computed property. When you wish to set its value to nil, set the backing property.

    Apple uses this technique in their Core Data templates. They use the same property name for both, but place an underscore before the backing property, which mimics an iVar from Objective-C.

    Something like this will do the trick:

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

    To use it:

    _fetchResultsController = nil
    self.fetchResultsController.performFetch()