Julian B.
Julian B.

Reputation: 3725

Static computed variable gets instantiated more than once

I have a date formatter I'm trying to create as a singleton within a UITableViewCell subclass so I've created a computed property like this:

private static var dateFormatter: NSDateFormatter {
    print("here here")
    let formatter = NSDateFormatter()
    formatter.dateFormat = "EEEE h a"
    return formatter
}

The problem is that I'm seeing the print statement more than once, which means it's getting created more than once. I've found other ways to do this (like putting in in an external class, or a class method), but I would love to understand what's happening here. Any ideas?

Upvotes: 14

Views: 5844

Answers (4)

denis_lor
denis_lor

Reputation: 6547

Static properties can be computed but also lazy, depending how you write them out.

- computed meaning that their value will be re-calculate each time they are called:

private static var dateFormatter: NSDateFormatter {
    print("here here") // prints each time dateFormatter its called
    let formatter = NSDateFormatter()
    formatter.dateFormat = "EEEE h a"
    return formatter
}

- lazy meaning that their value will be calculated once and only the first time they are called:

private static var dateFormatter: NSDateFormatter = {
    print("here here") // prints only one time, when called first time
    let formatter = NSDateFormatter()
    formatter.dateFormat = "EEEE h a"
    return formatter
}()

Upvotes: 1

Ian Rahman
Ian Rahman

Reputation: 315

It took me a while to find this question when attempting to figure out the difference between a static computed property in Swift that does include = and () and one that does not. @dan did a good job explaining what one should do to ensure a static property is computed only once, but in my own experimentation I came up with the following example that helped me visualize the differences between the two uses.

static var foo: String {
    print("computing foo")
    return "foo"
}

static var bar: String = {
    print("computing bar")
    return "bar"
}()

for _ in 1...3 {
    print(Class.foo)
    print(Class.bar)
}

The end result is:

computing foo
foo
computing bar
bar
computing foo
foo
bar
computing foo
foo
bar

foo has the benefits of a static property, including association with the type rather than a particular instance and the inability to be overridden by a subclass. bar, however, goes further by ensuring the property is only ever calculated one time.

Upvotes: 8

dan
dan

Reputation: 9825

Your snippet is equivalent to a get-only property, basically it's the same as:

private static var dateFormatter: NSDateFormatter {
    get {
        print("here here")
        let formatter = NSDateFormatter()
        formatter.dateFormat = "EEEE h a"
        return formatter
    }
}

If you only want it to run once you should define it the same way you would define a lazy property:

private static var dateFormatter: NSDateFormatter = {
    print("here here")
    let formatter = NSDateFormatter()
    formatter.dateFormat = "EEEE h a"
    return formatter
}()

Upvotes: 34

Alex Kosyakov
Alex Kosyakov

Reputation: 2176

Your static var is not a singleton, it is just a class method that creates and returns an instance of date formatter.

Check these answers of how to create a real sungleton: Using a dispatch_once singleton model in Swift

Upvotes: 0

Related Questions