Search code examples
swiftlazy-initialization

Difference between Lazy var and var as-a-closure in Swift


I've created some sample project to test out various types variable implementations to test out which ones are executed only once and which ones are executed every time called

class Something:NSObject
{
    var clock:Int = 0
    override var description: String
    {
        let desc = super.description
        clock += 1
        return "\(desc) Clock: \(clock)"
    }
}

static var staticVar:Something
{
    print("static Var")
    return Something()
}
static var staticVar2:Something = {
    print("static Var II")
    return Something()
}()

lazy var lazyVar:Something = {
    print("lazy Var")
    return Something()
}()

var simpleVar:Something {
    print("simple Var")
    return Something()
}

var simpleVar2:Something = {
    print("simple Var II")
    return Something()
}()

then in viewDidLoad()(to make sure variables are already initialized), called all the vars few times and saved in array to keep references strong

var strongArr = [Something]()

print("== STATIC VAR")
strongArr.append(ViewController.staticVar)
print(strongArr.last!.description)
strongArr.append(ViewController.staticVar)
print(strongArr.last!.description)
strongArr.append(ViewController.staticVar)
print(strongArr.last!.description)

print("\n== STATIC VAR {}()")
strongArr.append(ViewController.staticVar2)
print(strongArr.last!.description)
strongArr.append(ViewController.staticVar2)
print(strongArr.last!.description)
strongArr.append(ViewController.staticVar2)
print(strongArr.last!.description)

print("\n== SIMPLE VAR")
strongArr.append(self.simpleVar)
print(strongArr.last!.description)
strongArr.append(self.simpleVar)
print(strongArr.last!.description)
strongArr.append(self.simpleVar)
print(strongArr.last!.description)

print("\n== SIMPLE VAR {}()")
strongArr.append(self.simpleVar2)
print(strongArr.last!.description)
strongArr.append(self.simpleVar2)
print(strongArr.last!.description)
strongArr.append(self.simpleVar2)
print(strongArr.last!.description)

print("\n== LAZY VAR {}()")
strongArr.append(self.lazyVar)
print(strongArr.last!.description)
strongArr.append(self.lazyVar)
print(strongArr.last!.description)
strongArr.append(self.lazyVar)
print(strongArr.last!.description)

This is the result logged out in Console

== STATIC VAR
static Var
<_TtCC8DemoDemo14ViewController9Something: 0x600003725100> Clock: 1
static Var
<_TtCC8DemoDemo14ViewController9Something: 0x600003725160> Clock: 1
static Var
<_TtCC8DemoDemo14ViewController9Something: 0x600003725270> Clock: 1

== STATIC VAR {}()
static Var II
<_TtCC8DemoDemo14ViewController9Something: 0x6000037251b0> Clock: 1
<_TtCC8DemoDemo14ViewController9Something: 0x6000037251b0> Clock: 2
<_TtCC8DemoDemo14ViewController9Something: 0x6000037251b0> Clock: 3

== SIMPLE VAR
simple Var
<_TtCC8DemoDemo14ViewController9Something: 0x600003725240> Clock: 1
simple Var
<_TtCC8DemoDemo14ViewController9Something: 0x6000037252a0> Clock: 1
simple Var
<_TtCC8DemoDemo14ViewController9Something: 0x6000037252b0> Clock: 1

== SIMPLE VAR {}()
<_TtCC8DemoDemo14ViewController9Something: 0x600003738100> Clock: 1
<_TtCC8DemoDemo14ViewController9Something: 0x600003738100> Clock: 2
<_TtCC8DemoDemo14ViewController9Something: 0x600003738100> Clock: 3

== LAZY VAR {}()
lazy Var
<_TtCC8DemoDemo14ViewController9Something: 0x60000372ea70> Clock: 1
<_TtCC8DemoDemo14ViewController9Something: 0x60000372ea70> Clock: 2
<_TtCC8DemoDemo14ViewController9Something: 0x60000372ea70> Clock: 3

Based on these tests, looks like there is no difference between lazy var and simple var if both of them are defined as closures (() at the end).

Does variable implementation as closure automatically make a variable lazy or am I missing something?


Solution

  • The difference is when the init code for the variable is run. For lazy vars, the init code is run on first access of that variable. For non-lazy vars, it's run when the struct/class is initialized.

    struct N {
        lazy var a: Int = { print("Setting A"); return 5}();
        var b: Int = { print("Setting B"); return 5 }()
    }
    
    var n = N()
    print(n.a)
    print(n.b)
    

    Output:

    Setting B
    Setting A
    5
    5
    

    Note how non-lazy b is initialized first. a is only initialized when it's accessed. In either case, the initializer for each property is only run once.