Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Array of weak references to class bound protocol in Swift 4.1

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.

like image 556
FSMaxB Avatar asked Aug 12 '18 10:08

FSMaxB


1 Answers

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
like image 170
Martin R Avatar answered Oct 19 '22 13:10

Martin R