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