简体   繁体   中英

Swift Array holding elements weakly

I am taking some inspiration from

https://marcosantadev.com/swift-arrays-holding-elements-weak-references/

and I want to be able to maintain an array holding weak references to its elements, so that in case those elements get released elsewhere in my code base, I don't retain them in my array.

I would like the implementation to be as type safe as possible, however should be reusable.

The strategy that I am using is declaring a Weak Reference container as so.

class WeakRefContainer<T> where T: AnyObject {
    private(set) weak var value: T?

    init(value: T?) {
        self.value = value
    }
}

Then I want to maintain an array of these WeakRefContainers, so I create an array extension:

extension Array where Element: WeakRefContainer<AnyObject> {
    func compact() -> [WeakRefContainer<AnyObject>] {
        return filter { $0.value != nil }
    }
}

When calling the compact method, I am now able to clear up the array in case stuff needs to be cleaned up.

I am now having some compilation issues which am having trouble understanding. Lets suppose I have a sample class

class SampleClass {
}

And I try to use everything as follows:

var weakReferencesArray = [WeakRefContainer<SampleClass>]()
let obj1 = WeakRefContainer.init(value: SampleClass())
let obj2 = WeakRefContainer.init(value: SampleClass())
weakReferencesArray.append(obj1)
weakReferencesArray.append(obj2)

weakReferencesArray.compact()

When I try to call compact I get the following error message:

MyPlayground.playground:29:21: 'WeakRefContainer<SampleClass>' is not a subtype of 'WeakRefContainer<AnyObject>'

Can anyone unblock me please? Thanks

Your code doesn't work because WeakRefContainer<SampleClass> is not a subclass of WeakRefContainer<AnyObject> because generics are invariant in Swift. Thus weakReferencesArray can't use the compact method added from the extension.

There is a workaround for this, via a protocol:

protocol WeakHolder {
    var hasRef: Bool { get }
}

extension WeakRefContainer: WeakHolder {
    var hasRef: Bool { return value != nil }
}

extension Array where Element: WeakHolder {

    func compacted() -> [Element] {
        return filter { $0.hasRef }
    }

    mutating func compact() {
        self = compacted()
    }
}

I also renamed compact to compacted , for better Swift semantics, and replaced the original compact by a mutating version.

You probably want the extension to apply to all [WeakRefContainer<T>] where T can be any type extending AnyObject .

extension Array where Element: WeakRefContainer<T> {

However, currently, parameterised extensions are not possible. See this proposal .

You can kind of work around this by making compact generic:

extension Array{
    func compact<T>() -> [Element] where Element == WeakRefContainer<T> {
        return filter { $0.value != nil }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM