前言
Swift 开发者肯定都用过 lazy 关键字,它允许延迟属性的初始化,直到它们第一次被访问。这一特性在很多场景都特别有用,比如初始化高内存对象,合理利用 lazy 能有效提升应用的性能和资源利用率。今天来讲讲 lazy 关键字的使用场景和实现原理。
使用场景
场景一:延迟加载重量级对象
在许多应用中,某些对象的创建可能非常耗时,例如从数据库加载大量数据或进行复杂的计算。如果这些对象在启动时就被立即初始化,一方面可能会占用不必要的内存,另一方面可能会延长应用的启动时间。
class DatabaseManager {
lazy var databaseConnection: DatabaseConnection = {
print("创建数据库链接...")
return DatabaseConnection()
}()
}
let manager = DatabaseManager()
print("DatabaseManager 初始化成功")
// 在这一行访问 databaseConnection 之前,数据库连接不会被创建
print(manager.databaseConnection)
场景二:依赖于其他属性的初始化
有的时候,一个属性的初始化可能依赖于对象中其他属性的值,而这些属性可能在对象初始化时尚未准备好,举个例子,用户信息中 fullName 属性依赖 firstName 和 lastName 属性,所以 fullName 需要使用 lazy。
class UserProfile {
let firstName: String
let lastName: String
lazy var fullName: String = {
return "\(firstName) \(lastName)!"
}()
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
// 堆代码 duidaima.com
let profile = UserProfile(firstName: "John", lastName: "Doe")
print(profile.fullName) // 使用时才进行初始化
场景三:条件性资源加载
在某些情况下,对象的某些部分可能只在特定条件下才需要被加载。使用 lazy 可以确保只有在真正需要时才加载这些资源,从而节省内存和计算资源。
class FeatureManager {
lazy var advancedFeature: AdvancedFeature = {
print("Loading advanced feature...")
return AdvancedFeature()
}()
}
let featureManager = FeatureManager()
// 只有在满足某些条件时才访问 advancedFeature,触发其加载
if userHasAccessToAdvancedFeatures {
print(featureManager.advancedFeature)
}
lazy 关键字的底层实现
在 Swift 源码中,lazy 的实现细节可以在 Lazy.h[1] 文件中找到。简单解释一下这份 C++ 代码:
创建了两个模版类:LazyValue 用来存储懒加载值,Lazy 用来创建懒加载全局对象。
延迟初始化: LazyValue 在调用 get 方法时会先调用 Value.has_value() 方法检查是否初始化过,如果没有,则调用 Init() 来初始化对象。
一次性赋值: 一旦 lazy 属性被初始化,其值就会被存储起来,后续的访问将直接返回该值,无需重新计算。
需要注意的是,如果多个线程同时访问标有修饰符的 lazy 属性,并且该属性尚未初始化时,则不能保证该属性将仅初始化一次。
结论
Swift 中的 lazy 关键字提供了一种强大且灵活的方式来优化应用的性能和资源使用。通过延迟初始化,lazy 属性可以减少不必要的计算,提高应用启动速度,并按需加载资源。深入了解 lazy 的底层实现不仅有助于更好地利用这一特性,还能增进对 Swift 编译器和运行时行为的理解。