3
votes

I have a generic struct declared as follows:

struct WeakReference<T: AnyObject> {
    weak var value: T?

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

And a protocol:

protocol SomeProtocol: class {

}

But I'm not able to declare a variable of type of WeakReference<SomeProtocol>, the compiler complains that

'WeakReference' requires that SomeProtocol be a class type

Interestingly, in Swift, the class is a typealias of AnyObject.

I actually want to hold an array of WeakReference<SomeProtocol> because the array holds strong references.

Class-only generic constraints in Swift is a similar question but doesn't really solve this problem.

How can we pass the SomeProtocol to WeakReference?

EDIT: The following scenario compiles fine, but we lose the ability to hold weak reference:

struct Reference<T> {
    var value: T?

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

var array: [Reference<SomeProtocol>] = []
3

3 Answers

1
votes

Thats simple. You are passing SomeProtocol which is a protocol. You need to pass there specific class type.

Eample:

class SomeImplementation: SomeProtocol {
}

var weakSome: WeakReference<SomeImplementation> = ...

Or you can bypass it by marking the protocol with @objc annotation, but I am not a fan of this approach.

@objc protocol SomeProtocol: class {

}

var weakSome: WeakReference<SomeProtocol> = ...

Try checking this answer, it might provide you more context on the issue.

1
votes

What do you think about this approach?

class WeakReference<T> {
    weak var value: AnyObject?

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

protocol SomeProtocol: class {

}

class A: SomeProtocol { }

let araayOfSomeProtocolObjects: [SomeProtocol] = (0...5).map {_ in A() }
let arrayOfWeakReferences: [WeakReference<SomeProtocol>] = araayOfSomeProtocolObjects.map { WeakReference(value: $0) }

for item in arrayOfWeakReferences {
    print(item.value is A) // true
}

enter image description here

0
votes

I think this should solve your problem.

struct WeakReference<T> {
    private weak var privateRef: AnyObject?
    var ref: T? {
        get { return privateRef as? T }
        set { privateRef = newValue as AnyObject }
    }

    init(_ ref: T? = nil) {
        self.ref = ref
    }
}

// usage
protocol MyProto: class { }
extension UIViewController: MyProto { }

let vc = UIViewController()
var weakRef = WeakReference<MyProto>(vc)
print(weakRef.ref)

You obviously can use WeakReference with non class protocol or non bridged value types. If you try that, you'll get always nil.

P.S. : Try this code on a real Xcode project because in the Playground doesn't work as expected.