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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.