在数据类型中, 结构体被有意设计的不如类那么强大, 作为回报, 结构体提供了简洁性: 没有引用, 没有生命周期, 没有子类, 意味着我们不用考虑诸如循环引用, 副作用等问题. 而且简洁性也提供了更好的性能.
问题
目前项目中, 数据模型大部分采用struct类型, 但是有些复杂的数据模型, 例如首页的帖子列表数据, 包含了大量的属性, 我们知道, struct在传递时是直接copy的, 因此在首页列表滚动的过程中, 由于list view快速的给每一个cell传递model, 产生大量的copy操作, 影响到了列表页面的刷新帧率.
解决方式: 采用写时复制(copy-on-write)
顾名思义, 所谓的写时复制, 达到的效果就是一个结构体实例, 可以被多个对象拥有, 这些对象在读取数据时, 本质上读的是同一块内存地址, 只有在更改结构体时才会产生复制操作, 这样既保证了结构体的值类型特性, 又降低了上面所遇到的情况下, 大量复制结构体所产生的消耗.
实现
整体思路就是把一个class类封装到结构体中, 只在要更改结构体中的属性时才进行引用类型的复制操作, 如果只是作为读取操作进行传递值的话, 结构体也只是复制了引用类型的引用地址而已.
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct Clothes {
class Shirt {
var size: Int
init(size: Int) {
self.size = size
}
}
var shirt: Shirt
}
extension Clothes {
private var shirtForWriting: Shirt {
mutating get {
// isKnownUniquelyReferenced函数用来检查一个引用类型的实例是否只有一个所有者
if !isKnownUniquelyReferenced(&shirt) {
shirt = Shirt(size: shirtSize)
}
return shirt
}
}
var shirtSize: Int {
get { return shirt.size }
set { shirtForWriting.size = newValue }
}
}
使用写时复制的权衡
实际上, 写时复制是放弃了值类型不需要引用计数的这个优点, 来减轻值类型的复制语义这个特性所可能带来的成本, 增加或减少一个引用计数,都是一个相对较慢的操作 (这里的慢,比较的是把一些字节复制到栈上另一个位置之类的操作).