Swift 的 lazy 关键字一直有一个很容易被忽略的问题,那就是它不是线程安全的

官方文档中有这么一段描述:

Note
If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property hasn’t yet been initialized, there’s no guarantee that the property will be initialized only once.

官方自己也说了,如果多线程同时访问,可能导致初始化两次。

一般情况下我们不太会遇到这种问题,都是在 viewDidLoad 中添加、布局子视图,但是少数情况还是有可能会碰到这个问题,所以需要注意。

lazy var voiddispatch_once


Xcode 16 & Swift 6 以前

除了用 lazy 修饰 UI 视图之外,另一个可能比较常见的用法是用 lazy var void 代替 dispatch_once

在中文互联网上能搜到很多这种用法,但是读到这里你应该能发现,这种用法其实也是线程不安全的,因为它用的是线程不安全的 lazy 关键字修饰。

《Migrating to Swift 3》,官方明确提到了代替 dispatch_once 的办法:

The free function dispatch_once is no longer available in Swift. In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees as dispatch_once provided. Example:

 let myGlobal = { … global contains initialization in a call to a closure … }()
 _ = myGlobal  // using myGlobal will invoke the initialization code only the first time it is used.

请注意,示例中使用的是 let,并且是 lazily initialized globals 或者 static properties。众所周知,static 修饰或者顶层属性的初始化都是延迟初始化,再配合上线程安全的 let 定义,这才可以达到和 dispatch_once 相同的效果。

所以综上,建议使用 static let void 来代替 lazy var void