Swift(十一)-Swift中的lazy

4,052 阅读3分钟

「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战

延迟存储属性

Swift中支持将存储属性设置为延迟存储属性,所谓延迟存储属性,是指在类实例构造的时候,延迟存储属性并不进行构造或者初始化,只有当开发者调用类实例的这个属性时,此属性才完成构造或者初始化操作,这种机制将大大减少类实例的构造时间:

  • 延迟存储属性的初始值在其第一次使用时才进行计算。
  • 用关键字lazy来标识一个延迟存储属性;

延迟存储属性注意点

我们在创建延迟存储属性的时候需要注意两个地方:

  • 延迟存储属性必须有初始值:

image.png

  • 延迟存储属性必须使用var,不能使用let关键字:

image.png

一个延迟存储属性定义如下:

class Person {
    lazy var age: Int = 18
}

延迟存储属性的计算时机验证

我们在文章开头说过,延迟存储属性的初始值在其第一次使用时才进行计算,那么我们如何验证呢?我们来看下边的示例代码:

image.png

通过格式化输出p的内存地址可以看到其内存情况,前两个我们之前已经分析过是metadatarefCount,那么第三个就是我们age的值,此时还没有调用age,其值为0。我们向下执行一步:

image.png

通过打印结果我们发现,调用p.age之后,其值才存储进内存中;

延迟存储属性分析

我们生成SIL文件,来分析延迟存储属性age

image.png

生成SIL文件如下:

image.png

通过其在SIL文件中定义可以知道,延迟存储属性是一个可选类型,并且是final修饰的;

那么延迟存储属性如何实现上述第一次访问没值,第二次访问有值(也就是第一次使用才进行计算)的逻辑呢?我们在SIL文件中找到其访问机制的实现:

image.png

通过其初始化实现我们可以看到,给其设置了一个默认的枚举值Optional.none,类似OC中的nil,在Swift中也就是空;这就是我们第一次访问的时候其值没有设置的原因,那么第二次访问的时候是如何实现的呢?

我们在SIL文件中找到agegetter方法:

image.png

通过其实现中的枚举匹配分析得到:如果age有值就执行bb1代码块,如果age没值就执行bb2代码块;我们第一次查看的时候,其没值,所以我们先来分析bb2的代码块:

image.png

bb2代码块中主要是将值构建出来,赋给枚举变量;当我们第二次访问时就会有值,那么将会跳转到bb1代码块:

image.png

bb1代码块中将值直接返回;

延迟存储属性在使用是才会计算的机制本质上与懒加载是一样的,都可以节省内存空间;

延迟存储属性的线程安全性

延迟存储属性并不能确保只被访问一次,如果有多个线程在访问的时候,每一个线程都可能会调用到该属性,那么赋值操作就可能被执行多次,所以延迟存储属性并不是线程安全的;