重置CoreData持久性存储

时间:2010-02-17 11:26:16

标签: iphone multithreading core-data

基本上,我要做的是清除CoreData持久存储中的所有数据,然后导入新数据。你会怎么做?似乎最简单的解决方案是调用[NSPersistentStoreCoordinator removePersistentStore:error:]然后删除该文件。这是最好的做法吗?它是线程安全的吗?

非常感谢,

问题0.1:

我正在尝试更新CoreData持久性存储中的数据。我的用户正在查看包含统计数据的表格视图。我想通过删除所有现有数据,然后导入新数据来更新应用程序。我想展示一个进度视图,告诉用户应用程序没有挂起。

我在AppDelegate中添加了以下resetPersistentStore方法(persistentStoreCoordinator仅供参考):

// ...
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
// ...

/**
 Returns the persistent store coordinator for the application.
 If the coordinator doesn't already exist, it is created and the application's store added to it.
 */
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: kPersistentStoreFilename]];

    NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    

    return persistentStoreCoordinator;
}

/**
 * Will remove the persistent store
 */
- (NSPersistentStoreCoordinator *)resetPersistentStore {
    NSError *error;

    [managedObjectContext lock];

    // FIXME: dirty. If there are many stores...
    NSPersistentStore *store = [[persistentStoreCoordinator persistentStores] objectAtIndex:0];

    if (![persistentStoreCoordinator removePersistentStore:store error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }  

    // Delete file
    if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    } 

    // Delete the reference to non-existing store
    [persistentStoreCoordinator release];
    persistentStoreCoordinator = nil;

    NSPersistentStoreCoordinator *r = [self persistentStoreCoordinator];
    [managedObjectContext unlock];

    return r;
}

然后在我看来我做了(在另一个帖子中,因为我使用MBProgressHUD

PatrimoineAppDelegate *appDelegate = (PatrimoineAppDelegate *)[[UIApplication sharedApplication] delegate]; 
// Delete everything
[appDelegate resetPersistentStore];

我得到EXC_BAD_ACESS ...

我不太了解CoreData或多线程,也许我正在做一个明显的错误......

7 个答案:

答案 0 :(得分:10)

对于那些在iOS9 +上尝试此操作的用户,现在有destroyPersistentStoreAtURLreplacePersistentStoreAtURL API。

答案 1 :(得分:8)

如果您的目标是清空数据存储并使用新信息重新加载它,那么最好使用NSManagedObjectContext的reset,然后加载新数据。

来自NSManagedObjectContext's documentation

  

上下文始终具有“父”持久性存储协调器,该协调器提供模型并将请求分派给包含数据的各种持久性存储。如果没有协调员,上下文就不能完全发挥作用。上下文的协调器提供托管对象模型并处理持久性。从外部存储中提取的所有对象都与一个全局标识符(NSManagedObjectID实例)一起注册在上下文中,该标识符用于唯一标识外部存储的每个对象。

删除持久性存储并使用与存储关联的托管对象上下文可能是导致错误的原因。

答案 2 :(得分:5)

还是不行!由具有无效持久性存储的ManagedObjectContext链接导致的中止。最后,如果我删除ManagedObjectContext并让应用程序稍后重新创建

,它会起作用

这是我的修改

- (NSPersistentStoreCoordinator *)resetPersistentStore 
{
  NSError *error = nil;

  if ([persistentStoreCoordinator_ persistentStores] == nil)
    return [self persistentStoreCoordinator];

  [managedObjectContext_ release];
  managedObjectContext_ = nil;

  // FIXME: dirty. If there are many stores...
  NSPersistentStore *store = [[persistentStoreCoordinator_ persistentStores] lastObject];

  if (![persistentStoreCoordinator_ removePersistentStore:store error:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
  }  

  // Delete file
  if ([[NSFileManager defaultManager] fileExistsAtPath:store.URL.path]) {
    if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
      NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
      abort();
    } 
  }

  // Delete the reference to non-existing store
  [persistentStoreCoordinator_ release];
  persistentStoreCoordinator_ = nil;

  NSPersistentStoreCoordinator *r = [self persistentStoreCoordinator];

  return r;
}

答案 3 :(得分:4)

这是解决方案。可能有一些更优雅的选项(锁定...),但这个选项有效。

/**
 * Will remove the persistent store
 */
- (NSPersistentStoreCoordinator *)resetPersistentStore {
    NSError *error = nil;

    if ([persistentStoreCoordinator persistentStores] == nil)
        return [self persistentStoreCoordinator];

    [managedObjectContext reset];
    [managedObjectContext lock];

    // FIXME: dirty. If there are many stores...
    NSPersistentStore *store = [[persistentStoreCoordinator persistentStores] lastObject];

    if (![persistentStoreCoordinator removePersistentStore:store error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }  

    // Delete file
    if ([[NSFileManager defaultManager] fileExistsAtPath:store.URL.path]) {
        if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        } 
    }

    // Delete the reference to non-existing store
    [persistentStoreCoordinator release];
    persistentStoreCoordinator = nil;

    NSPersistentStoreCoordinator *r = [self persistentStoreCoordinator];
    [managedObjectContext unlock];

    return r;
}

答案 4 :(得分:0)

您可以换出(或删除)持久性存储,然后重置上下文(只需确保重新获取内存中的任何对象):

for (NSPersistentStore *store in persistentStoreCoordinator.persistentStores) {
    removed = [persistentStoreCoordinator removePersistentStore:store error:nil];
}

// You could delete the store here instead of replacing it if you want to start from scratch
[[NSFileManager defaultManager] replaceItemAtURL:storeURL
                                   withItemAtURL:newStoreURL
                                  backupItemName:nil
                                         options:0
                                resultingItemURL:nil
                                           error:nil];

NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];

[myContext reset];

我正在使用它来执行导入私有队列上的临时存储,然后覆盖主线程存储并在我完成时重新加载所有内容。我正在导入子上下文,但删除没有副作用的现有对象在我的情况下变得很麻烦。

答案 5 :(得分:0)

在重置persistentStore之前,必须先重置与之关联的所有managedObjectContext ,否则所有托管对象都不会有要访问的上下文,这可能会导致错误。

总是直接从文件系统中删除sqlite文件并将 managedObjectContext persistentStoreCoordinator 设置为 nil ,而不是调用removePersistentStore。 这将在您下次尝试访问或开始存储时重新创建persistantStore和managedObjectContext。

答案 6 :(得分:-4)

我找到了处理这些类型问题的最简单方法,只要您可以重新加载核心数据数据,就是按照以下步骤操作:

(1)重置模拟器。

(2)从项目中删除sqlite数据库。

(3)从您的机器上删除模拟器目录。

令人惊讶的是,我发现做了第1步&如果没有第3步,2并不总是有效。

如果您这样做,则需要重新加载Core Data商店。