Swift中协议扩展的默认实现不起作用

时间:2016-02-13 14:05:29

标签: swift generics swift-protocols

我尝试通过协议向NSManagedObject添加功能。我添加了一个工作正常的默认实现,但是一旦我尝试使用协议扩展我的子类,它就告诉我它的部分内容没有实现,即使我添加了默认实现。

任何人都知道我做错了什么?

class Case: NSManagedObject {

}

protocol ObjectByIdFetchable {
    typealias T
    typealias I
    static var idName: String { get }
    static func entityName() -> String
    static func objectWithId(ids:[I], context: NSManagedObjectContext) -> [T]
}

extension ObjectByIdFetchable where T: NSManagedObject, I: AnyObject {

    static func objectWithId(ids:[I], context: NSManagedObjectContext) -> [T] {
       let r = NSFetchRequest(entityName: self.entityName())
       r.predicate = NSPredicate(format: "%K IN %@", idName, ids)

       return context.typedFetchRequest(r)
   }
}

extension Case: ObjectByIdFetchable {

   typealias T = Case
   typealias I = Int

   class var idName: String {
       return "id"
   }

   override class func entityName() -> String {
       return "Case"
   }
}

我得到的错误是Type Case doesn't conform to protocol ObjectByIdFetchable

非常感谢。

2 个答案:

答案 0 :(得分:4)

我们将使用更简洁的示例(如下所示)来阐明这里出了什么问题。但是,关键的“错误”是Case无法使用objectWithId()的{​​{1}}的默认实现;因为类型... where T: NSManagedObject, I: AnyObject不符合类型约束Int。后者用于表示类型的实例,而AnyObject类型。

  

Int可以表示任何类类型的实例。

     

AnyObject可以表示任何类型的实例,包括函数类型。

来自Language Guide - Type casting

随后,Any无法访问blueprinted Case方法的任何实现,因此不符合协议objectWithId()

符合ObjectByIdFetchable的{​​{1}}到Foo:s的默认扩展名有效,因为T符合Any

Int

然而,将Any扩展为protocol Foo { typealias T static func bar() static func baz() } extension Foo where T: Any { static func bar() { print ("bar") } } class Case : Foo { typealias T = Int class func baz() { print("baz") } } :s符合Foo的情况也是如此,因为T不符合类型类通用{ {1}}:

AnyObject

编辑添加:请注意,如果您更改(正如您在自己的答案中发布的那样)

Int

AnyObject

然后自然protocol Foo { typealias T static func bar() static func baz() } /* This will not be usable by Case below */ extension Foo where T: AnyObject { static func bar() { print ("bar") } } /* Hence, Case does not conform to Foo, as it contains no implementation for the blueprinted method bar() */ class Case : Foo { typealias T = Int class func baz() { print("baz") } } 可以访问typealias T = Int 的{​​{1}}的默认实现,因为typealias T = NSNumber 是类型,符合Case

最后,请注意上面的示例,实现协议中蓝图的方法(例如,上例中的objectWithId()方法)不需要关键字... where T: NSManagedObject, I: AnyObjectNSNumber的扩展名是一个协议扩展(通过实现蓝图类型和方法符合AnyObject),并不能与超类的子类化override相提并论(在这种情况下,您可能希望覆盖超类方法)。

答案 1 :(得分:0)

我找到了问题的解决方案。我认为这是typealias T,这是不编译的原因。这实际上不是真的,我是对AnyObject说的,有趣的是Int不是AnyObject。我不得不将Int更改为NSNumber