「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」
延迟存储属性
在Swift中支持将存储属性设置为延迟存储属性,所谓延迟存储属性,是指在类实例构造的时候,延迟存储属性并不进行构造或者初始化,只有当开发者调用类实例的这个属性时,此属性才完成构造或者初始化操作,这种机制将大大减少类实例的构造时间:
- 延迟存储属性的初始值在其第一次使用时才进行计算。
- 用关键字
lazy来标识一个延迟存储属性;
延迟存储属性注意点
我们在创建延迟存储属性的时候需要注意两个地方:
- 延迟存储属性必须有初始值:
- 延迟存储属性必须使用
var,不能使用let关键字:
一个延迟存储属性定义如下:
class Person {
lazy var age: Int = 18
}
延迟存储属性的计算时机验证
我们在文章开头说过,延迟存储属性的初始值在其第一次使用时才进行计算,那么我们如何验证呢?我们来看下边的示例代码:
通过格式化输出p的内存地址可以看到其内存情况,前两个我们之前已经分析过是metadata和refCount,那么第三个就是我们age的值,此时还没有调用age,其值为0。我们向下执行一步:
通过打印结果我们发现,调用p.age之后,其值才存储进内存中;
延迟存储属性分析
我们生成SIL文件,来分析延迟存储属性age:
生成SIL文件如下:
通过其在SIL文件中定义可以知道,延迟存储属性是一个可选类型,并且是final修饰的;
那么延迟存储属性如何实现上述第一次访问没值,第二次访问有值(也就是第一次使用才进行计算)的逻辑呢?我们在SIL文件中找到其访问机制的实现:
通过其初始化实现我们可以看到,给其设置了一个默认的枚举值Optional.none,类似OC中的nil,在Swift中也就是空;这就是我们第一次访问的时候其值没有设置的原因,那么第二次访问的时候是如何实现的呢?
我们在SIL文件中找到age的getter方法:
通过其实现中的枚举匹配分析得到:如果age有值就执行bb1代码块,如果age没值就执行bb2代码块;我们第一次查看的时候,其没值,所以我们先来分析bb2的代码块:
在bb2代码块中主要是将值构建出来,赋给枚举变量;当我们第二次访问时就会有值,那么将会跳转到bb1代码块:
在bb1代码块中将值直接返回;
延迟存储属性在使用是才会计算的机制本质上与懒加载是一样的,都可以节省内存空间;
延迟存储属性的线程安全性
延迟存储属性并不能确保只被访问一次,如果有多个线程在访问的时候,每一个线程都可能会调用到该属性,那么赋值操作就可能被执行多次,所以延迟存储属性并不是线程安全的;