Swift 中 Lazy var 和 var as-a-closure 的区别
Difference between Lazy var and var as-a-closure in 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 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
根据这些测试,如果 lazy var 和 simple var 都被定义为闭包(()
最后),那么它们之间似乎没有区别。
作为闭包的变量实现会自动使变量惰性化还是我遗漏了什么?
区别在于变量的初始化代码是运行。对于 lazy
变量,首次访问该变量时初始化代码为 运行。对于 non-lazy
变量,当 struct/class 被初始化时它是 运行。
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
注意非惰性 b
是如何首先初始化的。 a
仅在访问时初始化。在任何一种情况下,每个 属性 的初始值设定项都只有 运行 一次。
当您将它们与结构的其他属性混合时,它们会变得更有趣 / class。以下是我能想到的一对:
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 不保证哪个 属性 会先被初始化。 firstName
和 lastName
可能在初始化 fullName2
时尚未初始化。
您不能定义包含惰性变量的结构的常量实例
let p = Person(firstName: "John", lastName: "Smith")
print(p.fullName1) // runtime error
A lazy var
是在您第一次阅读时计算的,因此根据定义,它会改变结构。因此 let p = Person(...)
无效。您必须使用 var p = Person(...)
.
但是,如果Person
是一个class,你可以使用let p = Person(...)
,因为这里的"constant"意味着p
指向一个固定的内存地址,但是该地址的对象可以随时更改。
我创建了一些示例项目来测试各种类型的变量实现,以测试哪些只执行一次,哪些每次调用都执行
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 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
根据这些测试,如果 lazy var 和 simple var 都被定义为闭包(()
最后),那么它们之间似乎没有区别。
作为闭包的变量实现会自动使变量惰性化还是我遗漏了什么?
区别在于变量的初始化代码是运行。对于 lazy
变量,首次访问该变量时初始化代码为 运行。对于 non-lazy
变量,当 struct/class 被初始化时它是 运行。
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
注意非惰性 b
是如何首先初始化的。 a
仅在访问时初始化。在任何一种情况下,每个 属性 的初始值设定项都只有 运行 一次。
当您将它们与结构的其他属性混合时,它们会变得更有趣 / class。以下是我能想到的一对:
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 不保证哪个 属性 会先被初始化。 firstName
和 lastName
可能在初始化 fullName2
时尚未初始化。
您不能定义包含惰性变量的结构的常量实例
let p = Person(firstName: "John", lastName: "Smith")
print(p.fullName1) // runtime error
A lazy var
是在您第一次阅读时计算的,因此根据定义,它会改变结构。因此 let p = Person(...)
无效。您必须使用 var p = Person(...)
.
但是,如果Person
是一个class,你可以使用let p = Person(...)
,因为这里的"constant"意味着p
指向一个固定的内存地址,但是该地址的对象可以随时更改。