Lazy collections feature in Swift

深入理解代替单纯记忆

当前最新Swift版本为Swift 5.10

之前一直没注意过Lazy Collection这个Swift特性,最近在看Design protocol interfaces in Swift-WWDC时偶然看到,查阅了一下资料,发现这个特性很早就有了,早在WWDC2018时官方在Using Collections Effectively session中就提到了

先对该特性做一个概括性的描述:

  • 该特性是Sequence protocol的一个属性---var lazy: LazySequence<Self> { get }
  • 该属性返回的值同样描述了一个Sequence,虽然元素和原Sequence是相同的
  • 但对返回的Sequence进行各种高阶函数操作时(如map、reduce、filter等等),就拥有了lazy(延迟计算)能力
  • 所谓延迟计算的特性,可以理解为,直到真正需要用到集合中的元素时(而非在此之前的某个时机),才会真正去遍历或读取集合中的元素,且尽可能只读取所需要的元素,避免多余的读取操作

我把官方文档中对lazy属性的描述原文贴出来:

A sequence containing the same elements as this sequence, but on which some operations, such as map and filter, are implemented lazily.

我认为其实但看上面的内容是比较难描述lazy的能力,通过下面的两个例子可以更容易理解

Demo1

swift 复制代码
let array = [1, 2, 3]

let mappedArray = array.map({
    print("map on array --- \($0)")
    return $0
})


let lazyMappedArray = array.lazy.map({
    print("map on lazy array --- \($0)")
    return $0
})

print(lazyMappedArray.first)

控制台输出结果为

c 复制代码
map on array --- 1
map on array --- 2
map on array --- 3
map on lazy array --- 1
Optional(1)

以上例子得出的结论是:

  • 普通的集合,在没有lazy能力加持下,当执行到array.map时就会对集合所有元素进行遍历
  • 有了lazy能力加持后,array.lazy.map执行时,map中的closure完全不会执行
  • 直到执行到lazyMappedArray.first时,map的closure才会执行。而且并不会遍历整个array,仅对第一个元素执行了closure

Demo2

另一个场景是,有些时候我们需要声明一个Readonly的属性,返回一个集合,

  • 该集合的创建可能需要遍历一个已有的集合
  • Readonly的属性可能需要在程序整个生命周期中执行多次,每次使用都会执行上面的逻辑
swift 复制代码
struct ABC {
    let array = [1, 2, 3]
    var mappedArray: [Int] {
        return array.map {
            print("map on array --- \($0)")
            return $0
        }
    }
    
    var lazyMappedArray:  LazyMapSequence<LazySequence<[Int]>.Elements, LazySequence<[Int]>.Element> {
        return array.lazy.map({
            print("map on lazy array --- \($0)")
            return $0
        })
    }
}

let abc = ABC()
var mappedArray = abc.mappedArray
var lazyMappedArray = abc.lazyMappedArray
abc.mappedArray
abc.lazyMappedArray

lazyMappedArray.first
lazyMappedArray.first

控制台输出结果是

sql 复制代码
map on array --- 1
map on array --- 2
map on array --- 3
map on array --- 1
map on array --- 2
map on array --- 3

map on lazy array --- 1
map on lazy array --- 1
  • 每次执行abc.mappedArray,array都会被遍历一遍
  • abc.lazyMappedArray则不会去遍历array,因为还没有使用lazyMappedArray中的元素
  • lazyMappedArray.first尝试读取集合元素时,才会真正执行map closure逻辑

总结

  • Lazy collection特性是通过Sequencelazy属性来提供给开发者使用,所以实现了Sequence协议的类型都支持lazy能力
  • 所谓延迟计算的特性,可以理解为,直到真正需要用到集合中的元素时(而非在此之前的某个时机),才会真正去遍历或读取集合中的元素,且尽可能只读取所需要的元素,避免多余的读取操作
  • lazy所赋予的延迟计算能力,在仅使用大数据量集合的部分元素、创建临时集合场景时,能显著降低额外性能损耗

参考

相关推荐
大熊猫侯佩8 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(五)
swiftui·swift·apple watch
大熊猫侯佩1 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(三)
数据库·swiftui·swift
大熊猫侯佩1 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(二)
数据库·swiftui·swift
大熊猫侯佩1 天前
用异步序列优雅的监听 SwiftData 2.0 中历史追踪记录(History Trace)的变化
数据库·swiftui·swift
大熊猫侯佩1 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(一)
数据库·swiftui·swift
season_zhu2 天前
iOS开发:关于日志框架
ios·架构·swift
大熊猫侯佩2 天前
SwiftUI 中如何花样玩转 SF Symbols 符号动画和过渡特效
swiftui·swift·apple
大熊猫侯佩2 天前
SwiftData 共享数据库在 App 中的改变无法被 Widgets 感知的原因和解决
swiftui·swift·apple
大熊猫侯佩2 天前
使用令牌(Token)进一步优化 SwiftData 2.0 中历史记录追踪(History Trace)的使用
数据库·swift·apple
大熊猫侯佩2 天前
SwiftUI 在 iOS 18 中的 ForEach 点击手势逻辑发生改变的解决
swiftui·swift·apple