Swift 延迟加载(懒加载)

Lazy 概念

  • 在Swift中,属性可以声明为延迟加载。延时加载属性就属于懒加载编程的一种,在对象构造时属性并不会被赋值,只有当使用到时才对属性进行赋值。
  • 懒加载是编程中常用的一种优化技巧。很多时候类示例中的属性是否真正创建与用户的操作逻辑有关,例如复杂对象的某个属性可能需要从本地文件进行读取,这是一个耗时的过程,并且并非用户每次都会用到这个属性,这时就可以采用懒加载的方式,只有当使用到这个属性时才进行文件的读取。
  • 懒加载可以有效地加快对象的构造速度并且可以在一定程度上节省内存的使用。

使用

OC 中可能是这样的:

@property (copy, nonatomic) NSString *name;

- (NSString *)name {
    if (!_name) {
        _name = @"sampson";
    }
    return _name;
}

swift 中我们在使用 lazy 修饰属性时,必须声明属性是变量(var),不能使用let关键字。而且我们需要显示的指定属性的类型。对该属性进行一个赋值语句用来首次访问时使用。

lazy var name: String = "sampson"

lazy var textLabel: UILabel = {
    let label = UILabel()
    label.text = "yo yo yo 柚子茶!"
    return label
}()

何时使用延迟加载?

  • 一种使用场景是,一个对象的属性的初始值依赖与其它的属性,所以必须先创建出这个对象,才能知道这个属性的值。
    举例来说,你有一个Person类以及一个personalizedGreeting属性。这个personalizedGreeting属性需要在对象创建完成后才延迟加载,因为只有在对象创建完成后它才能知道问候的人是谁(person的name)。请看代码:
class Person {
    
    var name: String
    
    lazy var personalizedGreeting: String = {
        [weak self] in
        return "Hello, \(self?.name ?? "XXX")!"
    }()
    
    init(name: String) {
        self.name = name
    }
}

注意,你必须使用 [weak self]来避免[循环引用]。[weak self]定义了一个在闭包中需要使用的、存在于闭包外的属性/变量列表,又叫捕获列表(capture list)。

当你实例化一个person时,他的问候语greeting此时并没有创建:

let person = Person(name: "sampson”)
// person.personalizedGreeting is nil

但是当你尝试打印出问候语时,这句问候语会自动生成出来:

NSLog(person.personalizedGreeting)
// personalizedGreeting is calculated when used
// and now contains the value "Hello, sampson!"
  • 另一种适合延迟加载的场景,是在属性的初始值需要进行大量计算之时。

举例来说,当你有个对象需要执行一个高负荷的算法来确定一张图片中的人脸个数,你可以将numberOfFaces属性设置为延迟加载。

或者当你有个类需要计算多个大数的值,你希望它们能在需要的时候才被计算出来:

class MathHelper {
    lazy var pi: Double = {
        // Calculate pi to an insane number of digits
        return resultOfCalculation
    }()
}

拓展

lazy 还可以配合 map filter 这类接受闭包运行的方法一起,使整个行为变成延迟进行的。

let numbers = 1...5
let doubleNumbers = numbers.map { (i: Int) -> Int in
    print("numbers\(i)")
    return i * 2
}

for i in doubleNumbers {
    print("doubleNumbers\(i)")
}

打印结果:

numbers1
numbers2
numbers3
numbers4
numbers5
doubleNumbers2
doubleNumbers4
doubleNumbers6
doubleNumbers8
doubleNumbers10

使用 lazy 后得到延迟运行版本的容器

let numbers = 1...5
let doubleNumbers = numbers.lazy.map { (i: Int) -> Int in
    print("numbers\(i)")
    return i * 2
}

for i in doubleNumbers {
    print("doubleNumbers\(i)")
}

打印结果:

numbers1
doubleNumbers2
numbers2
doubleNumbers4
numbers3
doubleNumbers6
numbers4
doubleNumbers8
numbers5
doubleNumbers10

对于一些不需要完全运行或者提前退出的情况,使用 lazy 进行性能优化更加有效。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容