13

I'm trying to create a generic WeakReference type that I can put into an array (and ultimately create a generic weak array type).

So far so good, but the following code:

class WeakReference<ElementType: AnyObject> {
    weak var element: ElementType?

    init(_ element: ElementType) {
        self.element = element
    }
}

protocol Element: AnyObject {}

class WeakElementHolder {
    var weakElements: [WeakReference<Element>] = []
}

Produces this compiler error:

WeakReference.swift:12:21: error: 'WeakReference' requires that 'Element' be a class type
    var weakElements: [WeakReference<Element>] = []
                       ^
WeakReference.swift:1:7: note: requirement specified as 'ElementType' : 'AnyObject' [with ElementType = Element]
class WeakReference<ElementType: AnyObject> {
  ^

This is strange because the Protocol definitely requires a class (AnyObject).

Strangely everything works fine if I leave out the generics:

protocol Element: AnyObject {}

class WeakElementReference {
    weak var element: Element?

    init(_ element: Element) {
        self.element = element
    }
}

class WeakElementHolder {
    var weakElements: [WeakElementReference] = []
}

Searching around I found this question but it wasn't really answered.

Is there a workaround to still somehow implement a generic array of weak references that works with class bound protocols?

UPDATE:

My concrete use case is to store a list of observers that get notified when something happens:

protocol Observer: AnyObject {
    func notify()
}

class WeakReference<ElementType: AnyObject> {
    weak var element: ElementType?

    init(_ element: ElementType) {
        self.element = element
    }
}

class WeakArray<ElementType: AnyObject> {
    var content: [WeakReference<ElementType>] = []
}

class Observable {
    var observers: WeakArray<Observer>()

    func notifyAllObservers() {
        // call notify() on all observers here
    }
}

These observers can be many different concrete types.

More Clarification: There is not only one Observer protocol, there are many that have nothing in common, this is why I want this to be generic in the first place.

1 Answer 1

5

As discussed in Protocol doesn't conform to itself?, the (non-@objc) protocol defined by

protocol Element: AnyObject {}

inherits from AnyObject, but does not conform to AnyObject.

A possible solution using a type-eraser:

protocol Observer: AnyObject {
    func notify()
}

struct AnyObserver {
    weak var base: Observer?

    init(_ base: Observer ) {
        self.base = base
    }
}

class Observable {
    var observers: [AnyObserver] = []

    func add(_ observer: Observer) {
        observers.append(AnyObserver(observer))
    }

    func notifyAllObservers() {
        for observer in observers {
            observer.base?.notify()
        }
    }
}

Example usage:

class A: Observer {
    func notify() {
         print("Hello A")
    }
}

class B: Observer {
    func notify() {
        print("Hello B")
    }
}

let a = A()
let b = B()

let o = Observable()
o.add(a)
o.add(b)

o.notifyAllObservers()
// Hello A
// Hello B
Sign up to request clarification or add additional context in comments.

7 Comments

But that only pushes around the problem, since I ultimately want to have an array of protocol impelementers, not concrete types.
The you probably need a type-eraser, as discussed in the Q&A that I linked to.
But then the entire point of the generic is gone again, because I would need a type eraser for every kind of observer and a generic weak array for every one of them.
Maybe it's just something that can't be expressed in swift.
@FSMaxB: You need only one type eraser for the Observable protocol, not one for each conforming type. See update.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.