如果 Core Data count/fetch 请求中的实体名称错误,如何避免崩溃?

How do I avoid a crash if entity name is wrong in Core Data count/fetch request?

我正在为我的所有持久性存储 class 编写通用基础 class。每个 child class 将在使用核心数据的持久数据库中与一个特定的 entity/table 一起工作。线程似乎工作正常,我可以正确获取 table 中的项目数。问题是如果获取请求中的实体名称是错误的,我不会得到异常,我会崩溃。由于这是一个字符串并且由程序员在代码中的某处键入,我想以某种更好的方式检测错误,以便提醒程序员使用了无效的实体名称。

这是我的代码:

class Store<EntityType:NSFetchRequestResult> : NSObject {
    private var entityName : String = ""
    init( entityName : String ) {
        self.entityName = entityName
    }

    public var Count : Int
    {
        get {
            var fetchResults : Int = 0
            objc_sync_enter( self )
            do {

                var privateContext : NSManagedObjectContext? = nil
                DispatchQueue.main.sync {
                    let deleg = UIApplication.shared.delegate as! AppDelegate
                    privateContext = deleg.privateManagedObjectContext
                }

                if privateContext == nil
                    { return 0 }

                privateContext!.performAndWait {
                    do
                    {
                        let request = NSFetchRequest<EntityType>( entityName: self.entityName )
                        fetchResults = try privateContext!.count( for: request )
                    } catch
                    {
                        print("Unexpected error: \(error).")
                    }
                }
            }
            objc_sync_exit( self )
            return fetchResults
        }
    }
...

使用错误的实体名称,MOC 上的 count() 函数会导致 SIGABRT 并且不会抛出任何异常。

如何以某种方式捕获错误?

我也愿意接受有关线程和在后台线程中使用它的评论。它现在可以工作,但是由于互联网和 Apple 都对如何在后台线程中使用 Core Data 含糊其词,因此不胜感激。

我刚才也试过了:

let request = NSFetchRequest<EntityType>( entityName: String(reflecting: EntityType.self) )

名字的格式是"app name.entityname",所以可能有用。但是由于编辑器允许程序员为实体和 class 输入不同的名称,这一点也不安全。如果我能以某种方式在运行时检查名称是否有效,我将使用此方法。但是不解决崩溃问题,我现在不愿意改变任何东西。

可以获取上下文模型中存在的实体名称列表。

这样,您可以在执行获取请求之前检查程序员提供的实体名称是否有效。

//get the context and make sure it's not nil
guard let privateContext = privateContext 
else { 
    print("Unexpected error: context is nil.")
    return 0 
}

//get the names of entities in the model associated with this context
// credit: Snowman, 
guard let names = privateContext.persistentStoreCoordinator?.managedObjectModel.entities.map({ (entity) -> String? in
    return entity.name
}) 
else { 
    print("Unexpected error: Could not get entity names from model.")
    return 0 
}

//make sure the name specified by the programmer exists in the model    
guard names.contains(where: { (name) -> Bool in
    return name == self.entityName
})
else {
    print("Unexpected error: \(self.entityName) does not exist in the model.")
    return 0 
}

privateContext.performAndWait {
    do 
    {
        let request = NSFetchRequest<EntityType>( entityName: self.entityName )
        fetchResults = try privateContext.count( for: request )
    } catch
    {
        print("Unexpected error: \(error).")
    }
}

如果您想了解性能:在我的测试中,为包含 20 个实体的模型检索实体名称列表 500 次需要 20 毫秒。不用担心。