19

实现在协议扩展中返回的静态协议函数时Self,在扩展中的函数实现时出现错误(没有上下文显示的最小简化场景):

import Foundation

protocol P {
    static func f() -> Self
    static func g() -> Self
}

extension P {
    static func f() -> Self { // Method 'f()' in non-final class 'NSData' must return `Self` to conform to protocol 'P'
        return g()
    }
}

extension NSData: P {
    static func g() -> Self {
        return self.init()
    }
}

替换SelfP发生错误的行会导致编译器出现段错误(sig 11)(这似乎是传达类型不匹配错误的有效方式)。

将声明更改f()为 return P,以及在错误行替换SelfP,会导致编译成功,但是会丢失类型精度(并且需要在每个调用站点强制向下转换,并Self详细记录要求)。

对于此问题,是否还有其他不丢失通用返回类型的解决方法?

编辑:补偿缺乏上下文的更多细节:P是一个公共协议,将由库公开,以符合(和覆盖)各种类型,g()因此覆盖不是一种选择。最好不必更改为协议扩展以外的其他内容,因为库内部在许多地方都使用了它。鉴于这两个选项,更改 to 的返回类型是更好的选择。f()NSDataf()f()P

更新

从 Swift 4(可能是 3)开始,上面的代码按原样工作。

4

6 回答 6

6

在 Swift 3 或 4 中:

import Foundation

protocol P {
    static func f() -> Self
    static func g() -> Self
}

extension P {
    static func f() -> Self {
        return g()
    }
}

extension Data: P {
    static func g() -> Data {
        return self.init()
    }
}

或者你可以用最终的子类替换你的类:

import Foundation

protocol P {
    static func f() -> Self
    static func g() -> Self
}

extension P {
    static func f() -> Self {
        return g()
    }
}

import Foundation

final class MyData: NSData {}
extension MyData: P {
    static func g() -> Self {
        return self.init()
    }
}

如果 NSData 是那些不容易被子类化的类集群之一(您将看到带有 的堆栈跟踪__CFRequireConcreteImplementation),您可能必须为真正的 NSData 创建最终类包装器,而不是使用子类。

于 2016-08-31T11:09:07.213 回答
1

从 Swift 2.1 开始,我能够通过仅让结构(或“最终”类)符合协议来避免给定的错误。目前,您可以将协议实现限制为引用类型(“仅限类”),但我还没有看到值类型的类似限制。

不过,在这种特殊情况下,我会说委托模式会适合。如果合适,您可以将 f 的默认实现移动到协议扩展中,并让子类实现覆盖委托属性。

protocol P {
    static var delegate : (() -> Self)?;
}

extension P {
    static func f() -> Self {
        // Use delegate if available, or use default implementation
    }
}

extension NSData : P {
    static var delegate : (() -> NSData)? = subImplementation;

    func subImplementation() -> NSData {
        // Some NSData-specific implementation
    }
}
于 2016-03-21T19:37:31.053 回答
1

这对我有用....

protocol P {
    static func foo()->Self
}

class C {
    required init() {}
}
extension C: P {
    static func foo() -> Self {
        return self.init()
    }
}

let c = C()
let c2 = C.foo()
print(c.dynamicType, c2.dynamicType) // C C

好的,我看到你注意到了,所以我更新了

protocol P {
    static func foo()->Self
    static func bar()->Self
}
extension P {
    static func foo() -> Self { // Method 'f()' in non-final class 'NSData' must return `Self` to conform to protocol 'P'
        return bar()
    }
}
// the class C must be final ...
final class C {
}
// otherwise the compiler can not decide the type of Self in 
extension C: P {
    static func bar() -> Self {
        return self.init()
    }
}

let c = C()
let c2 = C.foo()
print(c.dynamicType, c2.dynamicType) // C C

使用 NSData,如果你想做同样的事情,麻烦的是把 NSData 声明为 final。

于 2015-12-15T14:22:49.283 回答
1

您需要在 NSData 扩展中覆盖 f() 。

基本问题是(我认为)编译器不知道Selff在协议扩展中编译时是什么,我认为它假设它也必须是它应用它的类的确切类型。对于 NSData,情况可能并非如此,因为您可能有它的子类。

于 2015-12-15T15:19:14.750 回答
0

您也可以通过使用关联类型来绕过它。

protocol P {
    associatedtype Entity
    static func f() -> Entity
    static func g() -> Entity
}

extension P {
    static func f() -> Entity {
        return g()
    }
}

extension Data: P {
    static func g() -> Data {
        return self.init()
    }
}

你不需要Entity在你的实现中指定,因为编译器会从你的返回类型中推断出来。

于 2019-01-28T04:35:56.230 回答
0

我也遇到了同样的问题,你解决了吗?

这是我出来处理特定情况的另一种方法。与其使用需要返回静态函数的协议Self,也许您可​​以考虑定义一个初始化器所需的协议。

像这样:

protocol SomeProtocol {
    init(someParameter: Int)
}

不要忘记用required关键字标记初始化器实现。

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {

    }
}

希望这会有所帮助。

于 2016-06-25T14:00:34.377 回答