Protocol func returning Self

2018-12-31 18:58发布

I have a protocol P that returns a copy of the object:

protocol P {
    func copy() -> Self
}

and a class C that implements P:

class C : P {
    func copy() -> Self {
        return C()
    }
}

However, whether I put the return value as Self I get the following error:

Cannot convert return expression of type 'C' to return type 'Self'

I also tried returning C.

class C : P {
    func copy() -> C  {
        return C()
    }
}

That resulted in the following error:

Method 'copy()' in non-final class 'C' must return Self to conform to protocol 'P'

Nothing works except for the case where I prefix class C with final ie do:

final class C : P {
    func copy() -> C  {
        return C()
    }
}

However if I want to subclass C then nothing would work. Is there any way around this?

8条回答
公子世无双
2楼-- · 2018-12-31 19:41

Just throwing my hat into the ring here. We needed a protocol that returned an optional of the type the protocol was applied on. We also wanted the override to explicitly return the type, not just Self.

The trick is rather than using 'Self' as the return type, you instead define an associated type which you set equal to Self, then use that associated type.

Here's the old way, using Self...

protocol Mappable{
    static func map() -> Self?
}

// Generated from Fix-it
extension SomeSpecificClass : Mappable{
    static func map() -> Self? {
        ...
    }
}

Here's the new way using the associated type. Note the return type is explicit now, not 'Self'.

protocol Mappable{
    associatedtype ExplicitSelf = Self
    static func map() -> ExplicitSelf?
}

// Generated from Fix-it
extension SomeSpecificClass : Mappable{
    static func map() -> SomeSpecificClass? {
        ...
    }
}
查看更多
琉璃瓶的回忆
3楼-- · 2018-12-31 19:43

Following on Rob's suggestion, this could be made more generic with associated types. I've changed the example a bit to demonstrate the benefits of the approach.

protocol Copyable, NSCopying {
    typealias Prototype
    init(copy: Prototype)
    init(deepCopy: Prototype)
}
class C : Copyable {
    typealias Prototype = C // <-- requires adding this line to classes
    required init(copy: Prototype) {
        // Perform your copying here.
    }
    required init(deepCopy: Prototype) {
        // Perform your deep copying here.
    }
    @objc func copyWithZone(zone: NSZone) -> AnyObject {
        return Prototype(copy: self)
    }
}
查看更多
登录 后发表回答