Swift 与'不相同;自我';

Swift 与'不相同;自我';,swift,nsobject,Swift,Nsobject,我想创建一个可转换的协议,并扩展NSObject子类来实现它。特别是: protocol DataConvertible { class func convertFromData(data:NSData) -> Self? func data() -> NSData } 我认为实施将非常简单: extension UIImage : DataConvertible { class func convertFromData(data:NSData) -

我想创建一个可转换的协议,并扩展
NSObject
子类来实现它。特别是:

protocol DataConvertible {

    class func convertFromData(data:NSData) -> Self?

    func data() -> NSData

}
我认为实施将非常简单:

extension UIImage : DataConvertible {

    class func convertFromData(data:NSData) -> Self? {
        let image : UIImage? = UIImage(data: data)
        return image
    }

    func data() -> NSData {
        return UIImagePNGRepresentation(self)
    }

}
但编译失败,错误为“UIImage”与“Self”不同。我错过什么了吗

有没有其他方法可以实现这样的协议?

免责声明:这是一种解决方法,不是理想的解决方案


协议中使用
typealias
而不是
Self
可以:

protocol DataConvertible {
    typealias Result

    class func convertFromData(data:NSData) -> Result?

    func data() -> NSData

}
然后从
convertFromData
的实现中返回
UIImage?
而不是
Self?

extension UIImage : DataConvertible {

    class func convertFromData(data:NSData) -> UIImage? {
        let image : UIImage? = UIImage(data: data)
        return image
    }

    func data() -> NSData {
        return UIImagePNGRepresentation(self)
    }

}

更新:虽然这并不严格强制执行
UIImage。convertFromData(data)
本身将返回一个
UIImage?
,但在某些情况下,它确实可以通过其他泛型强制执行(正如@hpique所指出的)

例如:

class Cache<T: DataConvertible where T.Result == T> { /* ... */ }

只要协议到位,它就会编译。但是,如果尝试创建
缓存
,它将失败,因为
Int
T.Result
)不等于
UIImage
T
)。

这里最好的解决方案可能是将其定义为
init()
方法。在协议中,这是确保调用方在子类不重写方法的情况下返回完全初始化类型的唯一方法,同时也给了调用方重写方法的机会

下面是一个很好的解释,解释了为什么您的协议和实现所做的“承诺”在所有情况下都不成立:

特别是如果您有一个子类
UIImage
,比如说
AnimatedImage
,它不重写协议方法,而让超类返回
UIImage
,而不是
Self

注意:这不起作用,但似乎应该这样做。Bug?

protocol DataConvertible {

    init?(usingData:NSData)

    func data() -> NSData

}

extension UIImage : DataConvertible {

    convenience init?(usingData: NSData) {
        self.init(data: usingData)
    }

    func data() -> NSData {
        return UIImagePNGRepresentation(self)
    }

}
答案“应该”是:

不幸的是,似乎有一个bug使操场repl崩溃。尽管它会使playway/REPL崩溃,但它似乎在应用程序中也能工作

尽管它的实用性有限,因为调用的代码也会使编译器崩溃:

let image = UIImage.convertFromData(data)       <--- works

let converter = UIImage.self as DataConvertible
let image = converter.convertFromData(data)     <--- crashes

我怀疑问题在于UIImage是一个类集群,UIImage(数据:)不是一个真正的初始值设定项,而是一个类方法
[UIImage imageWithData:

-1,因为这让您的协议实现返回
UIImage
(或它们自己的类型,不管是什么),但它并不强制执行它。例如,尝试更改
convertFromData()
的实现以返回
Int
,只返回
2
而不是
image
。它不会抱怨-不幸的是,这种方法并不比在协议定义中简单地返回
AnyObject
要好。@Craigot很公平(感谢对否决票的解释)。我主要是寻找一种指定协议的替代方法。但你是对的,你失去了所有的执行力。是的,我自己也在考虑解决办法。罗布·纳皮尔(Rob Napier)在这里对OP的协议和实现为什么不能按照他想要的方式工作有一个非常好的解释:@CraigOtis我相信你的-1是不值得的。您可以通过使用通用需求来强制该类型,如下所示:。是的,它并不完美,但我们正在讨论一个快速缺陷/限制的解决方法。@CraigOtis这是堆栈溢出协作的一个很好的例子。我喜欢这个网站。:)是的,斯威夫特还没有准备好进入黄金时段。仍然有太多的方法会使编译器崩溃或混淆,更不用说REPL/player了。
class func convertFromData(data:NSData) -> Self? {
    return self(data:data)
}
let image = UIImage.convertFromData(data)       <--- works

let converter = UIImage.self as DataConvertible
let image = converter.convertFromData(data)     <--- crashes
class Fred {
    var fred:Int

    required init(value:Int) {
        fred = value
    }
}

extension Fred {
    class func makeOne(value:Int) -> Self {
        return self(value:value)
    }
}