我有一个核心数据类List
,它继承自抽象类型Synchronizable
。后者是我打算与服务器同步的其他几个类的父类。
我想在Synchronizable
中分解一个类函数,该类函数返回服务器上具有特定ID的对象:
class func withIDOnServer(_ pk:String, inMOC context:NSManagedObjectContext -> Self?
并将其用作
List.withIDOnServer(pk:"1234", context)
我的问题是我无法将结果转换为子类类型。这是我的代码:
extension Synchronizable {
// return the object with ID xxx on server.
class func withIDOnServer(_ pk:String, inMOC context:NSManagedObjectContext) throws -> Self? {
let entityName = String(describing: self)
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
request.predicate = NSPredicate(format:"'pk' = %@", pk)
do {
let results = try context.fetch(request) as! [Self]
guard results.count == 1 else {
if results.count > 1 {
throw fetchError.moreThanOne(pk: pk)
}
else { // results.count == 0
return nil
}
}
return results.first as! Self?
}
catch let error {
throw fetchError.cannotFetch(error.localizedDescription)
}
}
}
问题是函数主体中使用Self
。 (我也尝试过Self?
和type(of:self)
,不走运)
如何返回与子类相同类型的对象?是使用协议(在函数体中允许Self
的唯一方法吗?
答案 0 :(得分:1)
考虑一下,在基类上声明返回类型为Self
的函数没有任何意义。除非子类重写该函数,否则在子类上调用该函数将调用基类的实现,并且它无法根据哪个类是self
来神奇地更改其返回类型。
假定核心数据获取请求返回List
,这是此函数应返回的对象类型。您可以确定查询结果是List
的所有实例吗?如果不是这样,将结果强制转换为List
至少是不安全的。
有了此函数可以返回List
或Synchronizable
,您可以考虑向每个子类添加一个将其转换为子类的函数,例如如果Foo
是子类,它可能会像(未经测试)
class Foo: Synchronizable
{
class functions getInstance(_ pk:String, inMOC context:NSManagedObjectContext) throws -> Foo?
{
guard let ret = try withIDOnServer(pk, context: context) as? Foo
else { throw MyError.classCast }
}
return ret
}
答案 1 :(得分:1)
这将需要Swift泛型起作用。代替Self
,使用Swift泛型类型。像
extension Synchronizable {
// return the object with ID xxx on server.
class func withIDOnServer<T:Synchronizable>(_ pk:String, inMOC context:NSManagedObjectContext) throws -> T? {
let entityName = String(describing: self)
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
request.predicate = NSPredicate(format:"'pk' = %@", pk)
do {
let results = try context.fetch(request) as! [T]
guard results.count == 1 else {
if results.count > 1 {
throw fetchError.moreThanOne(pk: pk)
}
else { // results.count == 0
return nil
}
}
return results.first as! T?
}
catch let error {
throw fetchError.cannotFetch(error.localizedDescription)
}
}
要让编译器推断正确的类型,在调用函数时,需要声明所需的类型以使其清晰。像
let myList: List? = Synchronizable.withIDOnServer("asdf", inMOC: context)