Tips: Weak Arrays
0x01 How
在通常情况下,假如元素是对象类型的话,集合类型会强引用其元素,而 struct
或者是其它基础数据类型如 String
、 Int
等则是值类型,直接复制并没有引用。所以为了打破强引用我们需要为元素创建一个包装:
final class WeakBox<A: AnyObject> {
weak var unbox: A?
init(_ value: A) {
unbox = value
}
}
使用 WeakBox
,我们可以将任何强引用变为弱引用:
let instance = UIView()
let weakReference = WeakBox(instance)
为了在数组上实现弱引用,可以简单包装所有元素:
let strongArray = [UIView(), UIView()]
let weakArray = strongArray.map { WeakBox($0) }
为了更好地抽象出 WeakBoxes 中包装与解包的相关方法,可以给数组加多一层封装:
struct WeakArray<Element: AnyObject> {
private var items: [WeakBox<Element>] = []
init(_ elements: [Element]) {
items = elements.map { WeakBox($0) }
}
}
同时为了能够让 WeakArray 像 Swift 中的 Array 一样具有相同的特性,使用和 Array 一样的接口方法,我们可以使 WeakArray 遵循 Collection 协议:
extension WeakArray: Collection {
var startIndex: Int { return items.startIndex }
var endIndex: Int { return items.endIndex }
subscript(_ index: Int) -> Element? {
return items[index].unbox
}
func index(after idx: Int) -> Int {
return items.index(after: idx)
}
}
那么现在我们就能像使用其它集合类型一样使用 WeakArray,例如使用 filter
、 first
、 last
等方法:
let weakArray = WeakArray([UIView(), UIView()])
let firstElement = weakArray.filter { $0 != nil }.first // 猜猜结果使多少
0x02 However
然而上述代码中 firstElement
的值为 nil
???
我们可以回到最初 WeakBox 的实现代码,对比以下代码:
let instance = UIView()
let weakReference = WeakBox(instance)
// let instance = UIView()
let weakReference = WeakBox(UIView())
是不是大概知道了些什么?原因就是 WeakBox 并不持有对象,对象在外部也没有引用计数,那么刚创建完就自动释放掉了~
0x03 使用场景🤔
- 不采用
Notification
方式手动实现观察者模式 - etc