非 final 类中的方法必须返回 `Self` 以符合协议。

22

当在协议扩展中实现返回 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()
    }
}

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

f()的声明更改为返回P,并在错误行上用P替换Self会导致成功编译,但会失去类型精度(并且需要在每个调用点进行强制向下转换,还需要详细记录Self的要求)。

是否有其他解决此问题的方法,而不会失去通用返回类型?

编辑:进一步提供上下文信息: P是一个公共协议,将由库公开,用于各种类型遵循(并覆盖g()),因此在NSData中重写f()不是一个选项。还最好不必将f()更改为除协议扩展之外的其他内容,因为它在库内部的许多地方都被使用。考虑到这两个选项,将f()的返回类型更改为P是一个更好的选择。

更新

从Swift 4(可能是3)开始,上述代码可以正常工作。


2
导致编译器崩溃(sig 11)(这似乎是传达类型不匹配错误的有效方式)。请确保向苹果报告此问题。 - JeremyP
6个回答

8

在 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的栈跟踪),那么您可能需要创建一个最终类包装器来代替使用子类。


我按照你的建议将我的类声明为final,解决了我的问题。谢谢! - Wooseong Kim
你确定已经测试过了吗?然而在Swift 3中它并不起作用。它仍然会抱怨“非最终类‘Data’中的方法'g()'必须返回Self以符合协议'P'。” - Ozgur Vatansever
@OzgurVatansever 我不记得了,但我可能做过。我现在没有 Swift 3,但我已经编辑了包含完整示例的内容,可以在 Swift 4 中编译。 - Jano
谢谢你的回答,@Jano。在我的情况下,将类设置为final可以解决问题,但我想知道为什么会这样?我的意思是为什么final类可以解决这个问题,而非final类会出现错误? - Ram Patra

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。


这对我有什么帮助?我提供的示例是简化的,我不能在真实情况下删除函数g - Greg

1
截至Swift 2.1,我能够通过只让结构体(或“final”类)符合协议来避免给定的错误。目前,您可以将协议实现限制为引用类型(“仅类”),但我没有看到类似于值类型的限制。
在这种特殊情况下,我认为委托模式很合适。如果适当的话,您可以将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
    }
}

1
你需要在NSData扩展中重写f()方法。
基本问题是(我认为)编译器在协议扩展中编译f时不知道Self是什么,我认为它假设它必须是应用它的类的确切类型。对于NSData来说,这可能并不是真的,因为你可能有一个它的子类。

抱歉如果我表达不清楚;不幸的是,在NSData中覆盖f并不是一个选项,因为这只是许多类之一,将符合其中一些超出我的控制。我已经更新了问题并添加了更多细节。看起来我必须等待Swift 3.0才能在没有解决方法的情况下实现这一点... - Greg

1
您也可以通过使用关联类型来解决这个问题。
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,因为编译器会从返回类型中推断。


0

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

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

像这样:

protocol SomeProtocol {
    init(someParameter: Int)
}

不要忘记使用required关键词标记初始化实现。
class SomeClass: SomeProtocol {
    required init(someParameter: Int) {

    }
}

希望这可以帮到你。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接