我有一个相当标准的核心数据馈送 tableView,其中单元格数据从 fetchedResultsController 填充。
在我执行核心数据迁移之前,一切都按预期工作。轻量级迁移的目的是提供一个 简单备份不要更改模型。该商店使用 SQLite。计划是进行迁移以生成新的 数据文件,然后删除新存储并安装原始存储以保留原始文件名。
备份过程的 View 也是一个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
最佳答案
似乎正在发生的事情是:
- 您正在 TableView 中显示一些托管对象,这些对象是在
migratePersistentStore
调用之前获取的。 - 您执行
migratePersistentStore
调用。您的backupTheStore
方法将隐式删除原始持久存储(作为迁移调用的一部分),但它会尝试通过删除新的持久存储并重新添加旧的来修复问题。 - 然后您尝试使用第 1 步中的一个托管对象。
我认为,问题是尽管您重新添加了用于获取那些托管对象的持久存储,但是您的迁移过程已经失去了从托管对象到存储的连接。迁移调用清除持久存储协调器状态,这会中断托管对象/持久存储连接,并且添加持久存储不会重新创建该连接。 (也许它应该但显然不是它的设计方式)。
因此,您管理的对象无法与持久存储协调器关联到持久存储文件,并且当您尝试使用它们时会崩溃。
重新加载 TableView 是不够的,因为它只会从获取的结果 Controller 重新加载相同的托管对象。您还应该确保在获取的结果 Controller 上调用 performFetch
以使其重新获取其数据。如果这还不够,将 myFetchedResultsController
设置为 nil 并然后重新加载表,这样您将获得全新的提取。
关于ios - 核心数据迁移后主 TableView 崩溃 Swift,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31754094/