Lazy var 和 var as-a-closure 在 Swift 中的区别



我创建了一些示例项目来测试各种类型的变量实现,以测试哪些只执行一次,哪些每次调用都执行

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()
}()

然后在viewDidLoad()中(以确保变量已经初始化(,调用所有 var 几次并保存在数组中以保持引用强大

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)

这是在控制台中注销的结果

== 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

基于这些测试,如果惰性 var 和简单 var 都定义为闭包(最后()(,则看起来它们之间没有区别。

作为闭包的变量实现会自动使变量变得懒惰还是我错过了什么?

区别在于何时运行变量的初始化代码。 对于lazy变量,初始化代码在首次访问该变量时运行。 对于non-lazy变量,它在初始化结构/类时运行。

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)

输出:

Setting B
Setting A
5
5

请注意如何首先初始化非惰性ba仅在访问时初始化。 在任一情况下,每个属性的初始值设定项仅运行一次。

当您将它们与结构/类的其他属性混合时,它们会变得更加相互干扰。以下是我能想到的几个:

var-as-closure 不能引用其他实例变量

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
}
}

原因是 var-as-closure 是在初始化期间评估的,Swift 不保证哪个属性将首先初始化。 启动fullName2时,firstNamelastName可能尚未初始化。

不能定义包含惰性变量的结构的常量实例

let p = Person(firstName: "John", lastName: "Smith")
print(p.fullName1)                                      // runtime error

lazy var是在您第一次读取时计算的,因此根据定义,它会改变结构。因此let p = Person(...)无效。您必须使用var p = Person(...)

但是,如果Person是一个类,则可以使用let p = Person(...)因为这里的"常量"表示p指向固定内存地址,但该地址上的对象可以随时更改。

最新更新