背景
在一些类中经常会见到这样的写法:虽然使用的 var 修饰变量,但是只是在 init 方法中修改了一次,其他时候都是 get并没有重新 set。
1
2
3
4
5
6
7
class A {
var a = ""
init() {
a = "hahah"
}
}
通过资料查阅和文档查看,区别在于:var 修饰的变量锁指向的指针是可变的。但是官方仍然建议尽量多的使用 let。
那么区别到底有多少呢,相较于 let 可选的 var 性能又如何那。所以做了一个测试
测试
首先是3种定义方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class A {
var a = ""
init() {
a = "hahah"
}
}
class B {
let a: String
init() {
a = "hahah"
}
}
class C {
var a: String!
init() {
a = "hahah"
}
}
class D {
var a: String?
init() {
a = "hahah"
}
}
接下来对3种方式生成的对象进行 init 和 get 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//分别返回 a,b,c 强制解包方式和 c 非强制解包的测试时间(1000次)
func once() -> (TimeInterval, TimeInterval, TimeInterval, TimeInterval, TimeInterval, TimeInterval) {
let timea = Date.init().timeIntervalSince1970
for _ in 0 ... 999 {
let c = A()
cas.append(c)
res.append(c.a)
}
let timeb = Date.init().timeIntervalSince1970
for _ in 0 ... 999 {
let c = B()
cbs.append(c)
res.append(c.a)
}
let timec = Date.init().timeIntervalSince1970
for _ in 0 ... 999 {
let c = C()
ccs.append(c)
res.append(c.a!)
}
let timed = Date.init().timeIntervalSince1970
for _ in 0 ... 999 {
let c = C()
ccs.append(c)
if let s = c.a {
res.append(s)
}
}
let timee = Date.init().timeIntervalSince1970
for _ in 0 ... 999 {
let c = D()
cds.append(c)
res.append(c.a!)
}
let timef = Date.init().timeIntervalSince1970
for _ in 0 ... 999 {
let c = D()
cds.append(c)
if let s = c.a {
res.append(s)
}
}
let timeg = Date.init().timeIntervalSince1970
return(timeb - timea, timec - timeb, timed - timec, timee - timed, timef - timee, timeg - timef)
}
对上边的 1000 次测试运行 10 次取平均值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func check() {
var ats: Double = 0
var bts: Double = 0
var cts: Double = 0
var dts: Double = 0
var ets: Double = 0
var fts: Double = 0
for _ in 0 ... 10 {
let rst = once()
ats += rst.0
bts += rst.1
cts += rst.2
dts += rst.3
ets += rst.4
fts += rst.5
sleep(1)
}
print(ats)
print(bts)
print(cts)
print(dts)
print(ets)
print(fts)
print("\n")
}
结果
我进行了4次 check(),每次都是重新运行工程,手动点击开始测试,结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//6 0.009955167770385742
//1 0.007562875747680664
//5 0.008865833282470703
//4 0.00854039192199707
//2 0.007955312728881836
//3 0.0084686279296875
//6 0.00855565071105957
//1 0.006780147552490234
//4 0.007841110229492188
//2 0.0074846744537353516
//3 0.0075147151947021484
//5 0.00818324089050293
//6 0.008449316024780273
//1 0.0066416263580322266
//3 0.007724761962890625
//5 0.007788658142089844
//2 0.0074040889739990234
//3 0.007723331451416016
//6 0.009873151779174805
//1 0.0071184635162353516
//4 0.008206605911254883
//3 0.008171319961547852
//2 0.007562398910522461
//4 0.008209466934204102
结果为
1
2
3
4
5
6
24
6
16
14
9
15
结果分析
推荐使用的方法依次为:
第一名: let a: String
第二名: var a: String? 和取值 a!
第三名: 一共3种方式:
- var a: String 和取值 if let s = a { // do some … }
- var a: String? 和取值 if let s = a { // do some … }
- var a: String 和取值 a
最后一名: var a = “” 然后 赋值 a = xx 然后取值
总结
-
能尽量使用 let 时候就使用 let
-
不用过于纠结可选值的解包问题,对性能影响几乎不存在,所以建议使用 if let s = a { // do some … } 安全解包,或者使用 guard。