6

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>] = []
Shubham
  • 935
  • 1
  • 7
  • 25

3 Answers3

1

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.

Zdeněk Topič
  • 762
  • 4
  • 29
  • The problem is I want to hold an array of weak references of the classes that conform to protocol `SomeProtocol`. It works if we don't have the constraint ``. But then we can't have weak references. – Shubham Aug 06 '18 at 13:28
  • I have edited the the question to show this same case. – Shubham Aug 06 '18 at 13:42
  • 1
    I would suggest creating a specific `SomeWeakBox` where the `value` property will be `SomeProtocol` and definition of some protocol will be constrained to class types `protocol SomeProtocol: class`. Then you can simply have array `let array: [SomeWeakBox] = []` – Zdeněk Topič Aug 06 '18 at 14:10
1

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

shota
  • 388
  • 1
  • 8
  • How do I hold an array of weak references of objects which conform to `SomeProtocol` with this approach? – Shubham Aug 06 '18 at 13:29
  • This doesn't compile. Can't use `WeakReference`. That's the whole problem. – Shubham Aug 06 '18 at 13:40
  • @Shubham strange, i have compiled without problem on latest Xcode. Can you provide error image or something? – shota Aug 06 '18 at 13:44
  • @Shubham I also attached playground screenshot – shota Aug 06 '18 at 13:47
  • I'm sorry I overlooked that you removed the generic constraint in `WeakReference`. This compiles and works fine. The only downside is that we have to typecast the `value` to `SomeProtocol` every time we use it. – Shubham Aug 06 '18 at 14:19
1

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.

alfogrillo
  • 519
  • 5
  • 15