在协议扩展中实现返回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
会导致编译器出现段错误(第11页)(这似乎是传达类型不匹配错误的有效方法)。
将f()
的声明更改为返回P
,以及在错误行上用Self
替换P
会导致编译成功,但会丢失类型精度(并要求在每个呼叫站点强制转发,并详细记录Self
要求。
此问题的其他解决方法是否存在不丢失通用返回类型的问题?
编辑:补充缺少上下文的更多细节:P
是一个公共协议,将由库公开,以符合各种类型(并覆盖{{1 }}),所以在g()
中覆盖f()
不是一个选项。最好不要将NSData
更改为协议扩展之外的其他内容,因为库在内部的许多地方使用它。鉴于这两个选项,将f()
的返回类型更改为f()
是更好的选择。
从Swift 4(可能是3)开始,上面的代码按原样运行。
答案 0 :(得分:4)
在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创建一个最终的类包装器而不是使用子类。
答案 1 :(得分: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声明为最终版。
答案 2 :(得分:1)
您需要覆盖NSData扩展中的f()。
基本问题是(我认为)编译器在协议扩展中编译TransitionSpring
时不知道Self
是什么,我认为它必须是它的确切类型它正在应用它。对于NSData,可能不是这种情况,因为您可能有它的子类。
答案 3 :(得分: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
}
}
答案 4 :(得分:0)
我遇到了同样的问题,你明白了吗?
这是我出来处理特定情况的另一种方式。
您可以考虑定义初始化程序所需的协议,而不是使用需要返回protocol SomeProtocol {
init(someParameter: Int)
}
的静态函数的协议。
像这样:
required
不要忘记使用class SomeClass: SomeProtocol {
required init(someParameter: Int) {
}
}
关键字标记初始化程序实现。
public class DrawPixelTexture
{
private static readonly Dictionary<Color, Texture2D> texturesCache = new Dictionary<Color, Texture2D>();
public static void ClearCache()
{
texturesCache.Clear();
}
public static void Texture(Rect rect, Color colour, float opacity = 1)
{
colour.a = opacity;
Texture2D texture;
if (!texturesCache.TryGetValue(colour, out texture))
{
Debug.Log("still being created");
texture = new Texture2D(1, 1, TextureFormat.RGBA32, true);
texture.hideFlags = HideFlags.HideAndDontSave;
texture.SetPixel(0, 0, colour);
texture.Apply();
texturesCache.Add(colour, texture);
}
Graphics.DrawTexture(rect, texture);
}
}
希望这可以提供帮助。
答案 5 :(得分: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
,因为编译器将从返回类型中推断出它。