Reputation: 2181
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?
Upvotes: 4
Views: 2298
Reputation: 93151
They get more interresting when you mix them up with other properties of the struct / class. Here are a couple that I can think of:
struct Person {
var firstName: String
var lastName: String
lazy var fullName1 = "\(firstName) \(lastName)" // OK
var fullName2: String = { "\(firstName) \(lastName)" }() // invalid
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
The reason is that var-as-closure is evaluated during initialization and Swift does not guarantee which property will be initialized first. firstName
and lastName
may not have been initialized yet when fullName2
is initalized.
let p = Person(firstName: "John", lastName: "Smith")
print(p.fullName1) // runtime error
A lazy var
is calculated the first time you read it so by definition, it mutates the struct. Hence let p = Person(...)
is invalid. You must use var p = Person(...)
.
However, if Person
is a class, you can use let p = Person(...)
since "constant" here means p
points to a fixed memory address, but the object at that address can change anytime.
Upvotes: 2
Reputation: 4099
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.
Upvotes: 2