类函数返回Self?用于核心数据抽象类

时间:2018-10-09 11:09:17

标签: swift inheritance core-data return-type introspection

我有一个核心数据类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的唯一方法吗?

2 个答案:

答案 0 :(得分:1)

考虑一下,在基类上声明返回类型为Self的函数没有任何意义。除非子类重写该函数,否则在子类上调用该函数将调用基类的实现,并且它无法根据哪个类是self来神奇地更改其返回类型。

假定核心数据获取请求返回List,这是此函数应返回的对象类型。您可以确定查询结果是List的所有实例吗?如果不是这样,将结果强制转换为List至少是不安全的。

有了此函数可以返回ListSynchronizable,您可以考虑向每个子类添加一个将其转换为子类的函数,例如如果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)