这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战
引言
相信大家在日常开发时,一定用过关键字 Lazy。那使用它有什么好处呢?什么时候才需要使用呢,让我们一起来看看吧~
使用 Lazy 可以做很多事情:
- 实例化对象时不用给它的部分或全部成员初始值
- 调用之前,永远不会执行
- 存储计算值并防止重复计算
声明 Lazy
只要在成员变量前面添加一个 Lazy 关键字,就可以将其声明为 lazy。惰性成员必须声明为 var。苹果的说法:
You must always declare a lazy property as a variable (with the
varkeyword), because its initial value might not be retrieved until after instance initialization completes.
必须始终将惰性属性声明为可变的(使用 var 关键字) ,因为在实例初始化完成之前,可能无法检索其初始值
由于所有常量在初始化结束时都必须有一个值,因此有必要将惰性属性声明为 var,举个🌰,代码中的 fullname 是一个常量,初始化时没有赋值,就报错了。另一个heavyComputation被声明为 lazy,初始化时没有赋值,就不会报错。这个大家应该都理解。
通过把属性声明为 lazy,就减轻了初始化的工作。这就是懒加载了。
懒加载
另一个使用 lazy 的好处:加快初始化速度。作为开发者,我们都希望尽快能实例化我们的对象。因此,任何繁重的任务或计算,尽量避免在初始化时来做,也必须要避免。直到真正需要他们的时候,才去调用。让我们来看个例子。
从上面的例子可以明显看出,所有的计算都是在初始化 User 实例时完成的。这是因为 heavycalculation 被声明为 let 常量,需要在初始化结束时具有具体的值。因此,它是在同一时间计算。这并不是我们想要的。
我们把 heavycalculation 声明为 lazy,初始化时就不会计算它的值,直到第一次调用:
避免重复计算
我们也可以通过计算属性达到懒加载的效果:注意下面的代码和上面的代码的区别:
从这个示例中可以明显看出,计算属性在初始化时也没有被计算。
但这是否意味着计算属性和 lazy 属性相似呢?虽然看起来很相似,但是他们有一个非常基本的区别,这就使得 lazy 更加有用。
让我们试试多次访问计算属性,看看会发生什么。
从代码中发现,当我们多次访问计算属性ー heavycalculation 时,每次访问都会计算一遍(这里是两次)。
再来看看把 heavycalculation 声明为 lazy 时,再多次调用,看看会发生什么:
这次只计算该属性一次。这意味着,在第一次计算值之后,它的值就会被保存下来以备下次使用,并且与使用计算属性的情况不同,声明为 lazy 后,不会重复计算值。
因此,根据上面的示例,我们可以很容易地确定 lazy 属性存储值,并且不会在每次访问它们时进行计算。因此,节省了大量的处理器资源和时间。
使用注意事项
在理解了懒惰的工作原理之后,现在我们必须理解它的用法,并且在使用时要小心谨慎。
注意因为 lazy 属性只计算一次,因此内部逻辑不能依赖于其他变量,尤其是那些频繁变化的变量。这是因为,在第一次计算后,当其他的变量的值变化后,lazy 属性的值不能同步更新,使用时就会出错。
此外,lazy 不是线程安全的。这意味着当我们在不同的线程上访问同一个变量时,可能会得到不同的值。苹果的说法:
If a property marked with the
lazymodifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there’s no guarantee that the property will be initialized only once.
如果标记为 lazy 修饰符的属性被多个线程同时访问,而该属性尚未初始化,则不能保证该属性只初始化一次。
这表明,过多使用 lazy 实际上可能会产意想不到的错误。但有些啥时候,我们可能想懒散地做事。由于 UI 总是在主线程上刷新,这使得它成为懒加载的一个很好的选项。
总结
lazy 大家都很熟悉了,但在平时开发时,也不能为了用而用,还是要根据实际情况,酌情使用。同时注意多线程的坑。