1

I'd like to have a generic weak reference to an object and parametrize it by a protocol that is class-bound.

Here is the code example that does not work in my case:

protocol Protocol: class { // also written as `protocol Protocol: AnyObject {`
    func function()
}


final class A: Protocol {
    func function() {}
}

final class Weak<Type> where Type: AnyObject {
    final private weak var property: Type?

    init(property: Type) {
        self.property = property
    }
}

let a = A()
let something = Weak<Protocol>(property: a) // error: 'Weak' requires that 'Protocol' be a class type

I get an error on last line: 'Weak' requires that 'Protocol' be a class type.

As Protocol will always be of class type (which is the same as AnyObject) shouldn't that be allowed by the compiler? Is it possible to resolve that issue with swift 4?

If not, is it a limitation that can be resolved in a future version of swift or is it something impossible that the type system can not allow to happen?

A not accepted solution is to use @objc to the protocol declaration as in:

@obc protocol Protocol: class {
    func function()
}

as this leads to limitations.

averello
  • 114
  • 1
  • 5

3 Answers3

2

You are saying:

final class Weak<Type> where Type: AnyObject {

So you yourself are requiring that Type be a class (because that is exactly what AnyObject means).

A protocol is not a class. It is a protocol.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Basically, this is just another case of my https://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself. As for the future, might be covered by Hamish's answer https://stackoverflow.com/a/43408193/341994 – matt Jul 26 '18 at 14:48
  • But there is no way to specify a reference type other than `AnyObject`. Currently in swift you can't just declare `final class Weak where Type: class`. – averello Jul 27 '18 at 07:03
  • But that’s just notation; even if you could, a protocol is not a class. Read the links I gave you... – matt Jul 27 '18 at 07:08
0

This is currently impossible because protocols do not conform to themselves. A protocol is not a type; it is a recipe for a type. As the simplest example of why this is hard, consider this situation:

protocol X {
    init()
}

func f<T: X>(type: T.Type) -> T {
    return type.init()
}

f(type: X.self)

This would create a value of type X, but would that be? This isn't allowed, and the mechanism that prevents it is that protocols do not conform to themselves. This crops up in a lots of other ways, and your example is one of them.

Some of this will be addressed when more of the Generics Manifesto is implemented, particularly the section Generalized Existentials, but currently what you want isn't possible. You can't have a "weak reference to some unknown thing." You need to know what that thing is.

The standard solution to this problem is a box, not a protocol. See Weak Arrays for an example.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • I have already found the objc.io Weak Arrays articles. A `WeakBox` has same exact limitation as the example in the question above. – averello Jul 27 '18 at 07:07
  • Agreed; you will need to know your type. You cannot just have a Weak "something." You need to know what that something is. – Rob Napier Jul 27 '18 at 13:05
0

If I understand your problem I design this solution

First I created a protocol called WeakProtocol, this will be our base of protocols

protocol WeakProtocol : AnyObject {}

After change your protocol to extends this new WeakProtocol

protocol Protocol: WeakProtocol {
    func function()
}

after your Weak class you need apply a WeakProtocol type example

final class Weak<Type> where Type: WeakProtocol {
    final private weak var property: Type?

    init(property: Type) {
        self.property = property
    }
}

Now to implement you don't need say it Protocol because the constructor receive the protocol.

Complete code is

protocol WeakProtocol : AnyObject {}

protocol Protocol: WeakProtocol {
    func function()
}

final class A: Protocol {
    func function() {}
}

final class Weak<Type> where Type: WeakProtocol {

    final private weak var property: Type?

    init(property: Type) {
        self.property = property
    }
}

let a = A()
let something = Weak(property: a)

You can too only change Protocol to extends AnyObject and Weak class to Type:Protocol, like this

protocol Protocol: AnyObject {
    func function()
}

final class A: Protocol {
    func function() {}
}

final class Weak<Type> where Type: Protocol {

    final private weak var property: Type?

    init(property: Type) {
        self.property = property
    }
}

let a = A()
let something = Weak(property: a)
ViTUu
  • 1,204
  • 11
  • 21