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

b1uwtaje  于 2022-11-06  发布在  Swift
关注(0)|答案(6)|浏览(188)

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

在发生错误的行上用P替换Self会导致编译器segfault(sig 11)(这似乎是传达类型不匹配错误的有效方式)。
f()的声明更改为返回P,以及在错误行中将Self替换为P,可以成功编译,但会丢失类型精度(并且需要在每个调用站点强制向下转换,并详细记录Self要求)。
对于此问题,是否有其他不会丢失泛型返回类型的解决方法?

EDIT:补充缺少上下文的详细信息:P是一个公共协议,它将由库公开,以使各种类型符合(并覆盖g()),因此在NSData中覆盖f()不是一个选项。最好不必将f()更改为协议扩展以外的内容,因为库在内部的很多地方都使用它。给定这两个选项,将f()的返回类型更改为P是更好的选择。

更新

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

u4vypkhs

u4vypkhs1#

在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的stacktrace),您可能必须为真实的的NSData创建一个最终的类 Package 器,而不是使用子类。

qij5mzcb

qij5mzcb2#

您需要在NSData扩展中覆盖f()。
基本的问题是(我认为)当编译器在协议扩展中编译f时,它不知道Self是什么,并且我认为它假设它必须是它所应用的类的确切类型。对于NSData,情况可能不是这样,因为你可能有它的子类。

yqhsw0fo

yqhsw0fo3#

这对我很有效。

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。

bksxznpy

bksxznpy4#

从Swift 2.1开始,我可以通过只让结构体(或'final'类)符合协议来避免这个错误。目前,你可以限制协议实现为引用类型(“classes-only”),但我还没有看到对值类型的类似限制。
在这个特殊的例子中,我认为委托模式是合适的。如果合适的话,你可以把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
    }
}
ttp71kqs

ttp71kqs5#

您也可以使用相关的型别来解决这个问题。

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,因为编译器将从您的返回类型推断它。

v09wglhw

v09wglhw6#

我也有同样的问题,你发现了吗?
这是我提出的另一种处理特殊情况的方法。也许你可以考虑定义一个需要初始化程序的协议,而不是使用需要返回Self的静态函数的协议。
就像这样:

protocol SomeProtocol {
    init(someParameter: Int)
}

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

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

    }
}

希望这能有所帮助。

相关问题