Blog

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 WeakBoxes, 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.

Stay up-to-date with our newsletter or follow us on Twitter.

Back to the Blog

recent posts