错误重建/重置核心数据

时间:2016-05-19 05:01:59

标签: ios swift core-data

如果轻量级迁移失败,我正在尝试重建核心数据数据堆栈,并将用户发送回登录屏幕。我通过将多对多关系更改为一对一来测试此问题。

首先,我在删除后添加新的persistentStoreCoordinator时使用了相同的URL(storeURL);但是,我在“try persistentStoreCoordinator.add ...”行中的rebuildCoreData()中收到一条错误,指出“无法在同一个商店添加两次”

其次,我决定通过附加“1”来更改新持久性存储中的url,因此它变为self.applicationDocumentsDirectory.URLByAppendingPathComponent(“SingleViewCoreData1.sqlite”)。这朝着正确的方向迈出了一步 - 没有错误,我能够回到登录屏幕。但是,在登录后尝试进行第一次保存后,我收到错误'此NSPersistentStoreCoordinator没有持久存储(模式不匹配或迁移失败)。它无法执行保存操作。'

我在这里做错了什么?

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
    // Create the coordinator and store
    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
    let options = [NSMigratePersistentStoresAutomaticallyOption: true,
                    NSInferMappingModelAutomaticallyOption: true]
    var failureReason = "There was an error creating or loading the application's saved data."
    do {
        try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options)
    } catch {
        // Report any error we got.
        var dict = [String: AnyObject]()
        dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
        dict[NSLocalizedFailureReasonErrorKey] = failureReason

        dict[NSUnderlyingErrorKey] = error as NSError
        let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
        NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
        self.rebuildCoreData()
    }

    return coordinator
}()

lazy var managedObjectContext: NSManagedObjectContext = {
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
    let coordinator = self.persistentStoreCoordinator
    var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = coordinator
    managedObjectContext.mergePolicy = NSRollbackMergePolicy //This policy discards in-memory state changes for objects in conflict. The persistent store’s version of the objects’ state is used

    return managedObjectContext
}()

// MARK: - Tearing down core data stack and rebuilding it in the case that a lightweight migration fails
func rebuildCoreData() {

    let storeURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
    do {
        try NSFileManager.defaultManager().removeItemAtURL(storeURL)
    } catch {
        print(error)
        abort()
    }

    for object in managedObjectContext.registeredObjects {
        managedObjectContext.deleteObject(object)
    }

    do {
        try persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: [NSMigratePersistentStoresAutomaticallyOption: true,
            NSInferMappingModelAutomaticallyOption: true])
    } catch {
        print(error)
        abort()
    }

    print("successfully rebuilt core data")
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let controller = storyboard.instantiateInitialViewController()
    self.window?.rootViewController?.presentViewController(controller!, animated: false, completion: nil)
}

更新 - 在catch块中更改了文件删除和编辑的持久性存储协调器逻辑

 lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
    // Create the coordinator and store
    var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
    let options = [NSMigratePersistentStoresAutomaticallyOption: true,
                    NSInferMappingModelAutomaticallyOption: true]
    var failureReason = "There was an error creating or loading the application's saved data."
    do {
        try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options)
    } catch {

        // Report any error we got.
        var dict = [String: AnyObject]()
        dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
        dict[NSLocalizedFailureReasonErrorKey] = failureReason

        dict[NSUnderlyingErrorKey] = error as NSError
        let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
        NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")

        //rebuilds core data
        coordinator = self.rebuildCoreData(coordinator)
    }

    return coordinator
}()

新的rebuildCoreData代码:

  // MARK: - Tearing down core data stack and rebuilding it in the case that a lightweight migration fails
func rebuildCoreData(coordinator: NSPersistentStoreCoordinator) -> NSPersistentStoreCoordinator {

    let persistentStoreParentPath = self.applicationDocumentsDirectory.path
    let fileEnumerator = NSFileManager.defaultManager().enumeratorAtPath(persistentStoreParentPath!)
    while let path = fileEnumerator?.nextObject() as? String {
        if path.hasPrefix("SingleViewCoreData.sqlite") || path.hasPrefix(".SingleViewCoreData.sqlite") {
            let pathToDelete = (persistentStoreParentPath! as NSString).stringByAppendingPathComponent(path)
            do {
                try NSFileManager.defaultManager().removeItemAtPath(pathToDelete)
            }
            catch _ {
                // Handle error removing file
            }
        }
    }

    for object in managedObjectContext.registeredObjects {
        managedObjectContext.deleteObject(object)
    }

    do {
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
        try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: [NSMigratePersistentStoresAutomaticallyOption: true,
            NSInferMappingModelAutomaticallyOption: true])
    } catch {
        print(error)
        abort()
    }

    print("successfully rebuilt core data")
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let controller = storyboard.instantiateInitialViewController()
    self.window?.rootViewController?.presentViewController(controller!, animated: false, completion: nil)

    return coordinator
}

1 个答案:

答案 0 :(得分:3)

CoreData堆栈在最近的iOS版本中不包含单个文件。您可以创建CoreData堆栈并检查文件系统,以明确查看文件的名称(我记得还有一个.shm.wal文件。话虽如此,当我清除文件时,我通常使用NSFileManager枚举商店文件父路径中的对象,并删除前缀为<StoreFileName>.sqlite.<StoreFileName>.sqlite的任何内容。您还可以考虑将商店文件放在子目录中,然后只删除整个子目录。

let persistentStoreParentPath = self.applicationDocumentsDirectory.path
let fileEnumerator = NSFileManager.defaultManager().enumeratorAtPath(persistentStoreParentPath)
while let path = fileEnumerator?.nextObject() as? String {
    if path.hasPrefix("SingleViewCoreData.sqlite") || path.hasPrefix(".SingleViewCoreData.sqlite") {
        let pathToDelete = (persistentStoreParentPath as NSString).stringByAppendingPathComponent(path)
        do {
            try NSFileManager.defaultManager().removeItemAtPath(pathToDelete)
        }
        catch _ {
            // Handle error removing file
        }
    }
}

上面的代码也有一些其他问题需要考虑。您正在初始化rebuildCoreData()的闭包内调用persistentStoreCoordinator,但您在persistentStoreCoordinator中直接和间接使用rebuildCoreData()(通过访问managedObjectContext使用persistentStoreCoordinator import { Component } from '@angular/core'; import { Person as Person1 } from './person1'; import { Person as Person2 } from './person2'; @Component({ selector: 'person1', template: '<person></person>', directives: [Person1] }) export class Person1Wrapper { } @Component({ selector: 'person2', template: '<person></person>', directives: [Person2] }) export class Person2Wrapper { } @Component({ selector: 'app', template: '<person1></person1><person2></person2>', directives: [Person1Wrapper, Person2Wrapper] }) export class App { } )。