multithreading 重置 CoreData 持久存储

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2280158/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 01:07:03  来源:igfitidea点击:

Reset a CoreData persistent store

iphonemultithreadingcore-data

提问by charlax

Basically, what I'm trying to do is to wipe out all data in my CoreData persistent store, then import new data. How would you do this? It seems that the simplest solution is call [NSPersistentStoreCoordinator removePersistentStore:error:]and then remove the file. Is that the best practice available? Is it thread-safe?

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

Thank you very much,

非常感谢,

#

#

Question 0.1: was

问题 0.1:是

I am trying to update the data in a CoreData persistent store. My user is seeing a table view with statistical data. I want to update the application by deleting all existing data, then importing new data. I would like to show a progress view to tell the user that the application is not hanging.

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

I have added the following resetPersistentStoremethod in my AppDelegate (persistentStoreCoordinatoris given for reference):

resetPersistentStore在我的 AppDelegate 中添加了以下方法(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;
}

Then in my view I do (in another thread since I am using MBProgressHUD:

然后在我看来我这样做(在另一个线程中,因为我正在使用MBProgressHUD

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

And I get an EXC_BAD_ACESS...

我得到一个EXC_BAD_ACESS...

I do not know CoreData or multithreading very well, maybe I'm doing an evident error...

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

采纳答案by Giao

If your goal is to empty the data store and reload it with new information, you may be better off using NSManagedObjectContext's resetand then loading in new data.

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

From NSManagedObjectContext's documentation

来自NSManagedObjectContext 的文档

A context always has a “parent” persistent store coordinator which provides the model and dispatches requests to the various persistent stores containing the data. Without a coordinator, a context is not fully functional. The context's coordinator provides the managed object model and handles persistency. All objects fetched from an external store are registered in a context together with a global identifier (an instance of NSManagedObjectID) that's used to uniquely identify each object to the external store.

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

Removing the persistent store and using the managed object context associated with the store is probably the cause of the error.

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

回答by Phil Loden

For those attempting this on iOS9+, there are now the destroyPersistentStoreAtURLand replacePersistentStoreAtURLAPIs.

对于那些在 iOS9+ 上尝试此操作的人,现在有destroyPersistentStoreAtURLreplacePersistentStoreAtURLAPI。

回答by SuryC

Still not work! Abort caused by ManagedObjectContext link with invalid persistance store. Finally it work if I delete the ManagedObjectContext and let the app re-create later

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

Here is my modification

这是我的修改

- (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;
}

回答by charlax

Here is the solution. There may be some more elegant options (lock...) but this one works.

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

/**
 * 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;
}

回答by Justin Driscoll

You can swap out (or delete) a persistent store and then reset the context (just make sure you refetch any objects you have in memory):

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

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];

I'm using this to perform an import into a temporary store on a private queue and then overwriting the main thread store and reloading everything when I'm finished. I was importing into a child context but deleting existing objects without side effects became troublesome in my case.

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

回答by Shub

Before resetting the persistentStore you must first reset all the managedObjectContextassociated with that or else all the managedObjects will not have context to be accessed this may cause error.

在重置persistentStore 之前,您必须首先重置所有与它相关联的managedObjectContext,否则所有managedObjects 都没有可以访问的上下文,这可能会导致错误。

it is good to always remove the sqlite file directly from file system and set managedObjectContextand persistentStoreCoordinatorto nil, rather than calling removePersistentStore. This will recreate the persistantStore and managedObjectContext next time you try to access or start storing.

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

回答by ardochhigh

I found the easiest way to deal with these types of problems, so long as you can reload your Core Data data, is to follow these steps:

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

(1) Reset the simulator.

(1) 重置模拟器。

(2) Delete the sqlite database from your project.

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

(3) Delete the simulator directory from your machine.

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

Surprisingly I found that doing steps 1 & 2 did not always work without step 3.

令人惊讶的是,我发现在没有步骤 3 的情况下,执行步骤 1 和 2 并不总是有效。

You will need to reload your Core Data store if you do this.

如果您这样做,您将需要重新加载您的核心数据存储。