我有一个相当标准的Core Data feed tableView,其中有一个单元数据从fetchedResultsController填充。

在进行核心数据迁移之前,一切都会按预期进行。轻量级迁移的目的是提供一个
简单备份不更改模型。商店使用SQLite。该计划是进行迁移以生成新的
数据文件,然后删除新存储并安装原始存储,以保留原始文件名。

备份过程的视图也是tableView。迁移完成后,新文件可见
在备份tableView中。单击“返回”按钮以返回原始tableView时,数据为
按预期可见,但是单击tableView中的任何行会导致立即崩溃,并且向我显示
可怕的“此NSManagedObjectContext的协调器无法访问对象的持久性存储”错误。

我已经为此苦了一个星期。我一定错过了一个基本概念。任何帮助,将不胜感激。 (iOS 8,Xcode 6.4)

这是fetchedResultsController变量。在进行迁移之前,这些操作一直都在起作用:

var myFetchedResultsController: NSFetchedResultsController? = nil

var fetchedResultsController: NSFetchedResultsController {

    managedObjectContext = kAppDelegate.managedObjectContext

    if myFetchedResultsController != nil {
        return myFetchedResultsController!
    }//if my ! nil

    let fetchRequest = NSFetchRequest()

    let entity = NSEntityDescription.entityForName("Patient", inManagedObjectContext: managedObjectContext)
    fetchRequest.entity = entity
    fetchRequest.fetchBatchSize = 50

    //Sort keys
    let sortDescriptor = NSSortDescriptor(key: "dateEntered", ascending: false)
    let sortDescriptors = [sortDescriptor]

    fetchRequest.sortDescriptors = [sortDescriptor]

    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)

    var countError : NSError? = nil
    var count = managedObjectContext.countForFetchRequest(fetchRequest, error: &countError)

    println("The count is \(count)")
    //after creating a backup, this count is ALWAYS zero - never the real count

    aFetchedResultsController.delegate = self
    myFetchedResultsController = aFetchedResultsController

    var error: NSError? = nil
    if !myFetchedResultsController!.performFetch(&error) {
         // Don't forget the code to handle the error appropriately.
         println("Unresolved error \(error), \(error!.userInfo)")
        //Remove this
         abort()
    }//if !my

    return myFetchedResultsController!
}//var fetchedResultsController


备份过程的两个功能:

func createLocalBackupFile() {

    let dateFormatter = NSDateFormatter()
    dateFormatter.dateFormat = "yyyyMMddHHmmss"
    let theDateTime = NSDate()
    let formattedDateTime = dateFormatter.stringFromDate(theDateTime)
    let backupFileName : String = "BiopBak" + formattedDateTime + ".sqlite"
    println("backupFileName is \(backupFileName)")

    let psu : CRSPersistentStoreUtilities = CRSPersistentStoreUtilities()//the function below is in this class

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
        //println("In a background queue, creating the backup file")
        psu.backupTheStore(backupFileName)

        //go back to the main queue
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            println("Back on main queue after creating the backup file")

            if (self.backupSqlFiles.count == 1 && self.backupSqlFiles[0] == "Placeholder for empty list") {
                self.backupSqlFiles.append(backupFileName.stringByDeletingPathExtension)
                self.backupSqlFiles.removeAtIndex(0)

            } else {
                self.backupSqlFiles.append(backupFileName.stringByDeletingPathExtension)

            }//if placeholder is only record in database - else

            self.tableView.reloadData()
            println("backupSqlFiles[] = \(self.backupSqlFiles)")

        })//back to main block - inner
    })//background processing block - outer

}//createLocalBackupFile

func backupTheStore(newSQLFileName : String) -> NSPersistentStore? {

        let storeType = NSSQLiteStoreType
        var migrateError : NSError?
        var currentStore : NSPersistentStore = kAppDelegate.persistentStoreCoordinator?.persistentStores.last! as! NSPersistentStore
        let options = [NSMigratePersistentStoresAutomaticallyOption: true,
            NSInferMappingModelAutomaticallyOption: true]

        let fileManager = NSFileManager.defaultManager()
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        let docsDir = paths[0] as! String
        let docsDirURL = NSURL(fileURLWithPath: docsDir)

        let originalStoreURL : NSURL = docsDirURL?.URLByAppendingPathComponent("BiopLogCloud.sqlite") as NSURL!
        var newStoreURL : NSURL = docsDirURL?.URLByAppendingPathComponent(newSQLFileName) as NSURL!

    kAppDelegate.persistentStoreCoordinator?.migratePersistentStore(currentStore, toURL: newStoreURL, options: options, withType: storeType, error: &migrateError)

    currentStore = kAppDelegate.persistentStoreCoordinator?.persistentStores.last! as! NSPersistentStore

    var removeStoreError : NSError?
    var theStores = kAppDelegate.persistentStoreCoordinator?.persistentStores

    if let theStores2 = theStores {
        for removeStore in theStores2 {
            var removed : Bool = true
            kAppDelegate.persistentStoreCoordinator?.removePersistentStore(removeStore as! NSPersistentStore, error: &removeStoreError)

            if (removeStoreError != nil) {
                println("Unable to remove persistent store \(removeStore)")
            }
        }//for in
    }//if let theStores

    var addStoreError : NSError?

    kAppDelegate.persistentStoreCoordinator?.addPersistentStoreWithType(storeType,
        configuration: nil,
        URL: originalStoreURL,
        options: options,
        error:&addStoreError)

    if (addStoreError != nil) {
        println("Unable to add persistent store \(originalStoreURL)")
        //change this to add a user alert
    }//if

    //this does not seem to do any good
    let ptvc : PatientTableViewController = PatientTableViewController()

    dispatch_async(dispatch_get_main_queue()) {
        () -> Void in
        ptvc.tableView.reloadData()
    }//block

    return thisStore

}//backupTheStore

最佳答案

似乎正在发生的是:


您正在表视图中显示一些托管对象,这些托管对象是在migratePersistentStore调用之前获取的。
您进行migratePersistentStore调用。您的backupTheStore方法将隐式删除原始的持久性存储(作为迁移调用的一部分),但是它尝试通过删除新的持久性存储并重新添加旧的持久性存储来解决问题。
然后,您尝试使用步骤1中的托管对象之一。


我认为问题是,尽管您已经重新添加了用于获取那些托管对象的持久性存储,但是您的迁移过程已经失去了从托管对象到存储的连接。 migration调用清除持久性存储协调器状态,这会中断托管对象/持久性存储连接,并且添加持久性存储不会重新创建该连接。 (也许应该,但这显然不是它的设计方式)。

结果,您拥有了持久性存储协调器无法与持久性存储文件关联的托管对象,并且在尝试使用它们时会崩溃。

重新加载表视图是不够的,因为它只会从获取的结果控制器中重新加载相同的托管对象。您还应该确保在获取的结果控制器上调用performFetch,以使其重新获取其数据。如果这还不够,请将myFetchedResultsController设置为nil,然后重新加载该表,这样您将获得一个全新的访存。

关于ios - 核心数据迁移后,主TableView崩溃后迅速,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31754094/

10-11 19:53
查看更多