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

时间:2015-12-15 13:01:33

标签: swift interface swift2 protocol-extension

在协议扩展中实现返回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)开始,上面的代码按原样运行。

6 个答案:

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