Swift Tip: Weak Arrays
In Swift Talk 81 we created a queue that only references the objects in the queue weakly. Normally a collection references its elements strongly (we're assuming that the elements are objects), so we need to create a wrapper around each element that breaks the strong reference:
final class WeakBox<A: AnyObject> {
weak var unbox: A?
init(_ value: A) {
unbox = value
}
}
Using WeakBox
we can turn any strong reference into a weak one:
let instance = UIView()
let weakReference = WeakBox(instance)
In order to make an array reference its elements weakly, we simply wrap all the elements:
let strongArray = [UIView(), UIView()]
let weakArray = strongArray.map { WeakBox($0) }
To abstract away the mechanics of wrapping and unwrapping elements in WeakBox
es, we can introduce a lightweight wrapper around an array that takes care of that for us:
struct WeakArray<Element: AnyObject> {
private var items: [WeakBox<Element>] = []
init(_ elements: [Element]) {
items = elements.map { WeakBox($0) }
}
}
To benefit from all the usual functionality we're used to from Swift collections, we make WeakArray
conform to the Collection
protocol:
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)
}
}
Now we can use WeakArray
like any other collection, e.g. calling filter
on it, or using its first
and last
properties
let weakArray = WeakArray([UIView(), UIView()])
let firstElement = weakArray.filter { $0 != nil }.first
Can you guess the value of firstElement
?
For an application of this technique, watch last week's Swift Talk or read the transcript. This week, we're taking a holiday break and won't release a new episode.