当我只是在内存(而不是磁盘)中存储数据时,如何有效地擦除我的核心数据存储?

时间:2022-09-25 12:32:16

I'm using Core Data with an in-memory store, and I want to wipe it completely at some point. Most of the questions that I've found that relate to this are in regards to an on-disk store and involve deleting the store file or all the managed objects.

我在内存存储中使用核心数据,我想在某个时候彻底清除它。我发现的与此相关的大多数问题都与磁盘存储有关,涉及删除存储文件或所有托管对象。

Is there a simpler way when it's in-memory? Can I just set something to nil and be done with it?

在内存中有更简单的方法吗?我可以把一些东西设为nil然后用它来做吗?

3 个解决方案

#1


9  

In the case of an in-memory store remove the store from the NSPersistentStoreCoordinator using removePersistentStore:error:. At that point you can create a new in-memory store attached to the coordinator if you want, which will give you a blank slate at the store level - which seems to be the focus of your question.

在内存中存储的情况下,使用removePersistentStore:error从NSPersistentStoreCoordinator中删除存储。此时,如果您愿意,您可以创建一个附加到协调器的新的内存中存储,这将为您提供一个存储级别的空白记录—这似乎是您的问题的重点。

Note that if you are retaining managed objects that you fetched from an NSManagedObjectContext those may still stick around, and when a fault is fired on them after you have removed your in-memory store they can throw exceptions. It's advisable that if this is likely (usually because you are using a single context, or are not good about using Instruments to track memory use) that you also call reset on your managed object context(s) and remove any lingering strong references to managed objects.

注意,如果您正在保留从NSManagedObjectContext中获取的托管对象,这些对象可能仍然存在,并且当在删除内存存储之后对它们触发错误时,它们可能会抛出异常。如果这是可能的(通常是因为您正在使用一个上下文,或者不善于使用工具来跟踪内存使用),您也可以在托管对象上下文上调用reset,并删除对托管对象的任何持久强引用。

So using your code: When you create the in-memory store, specify a URL so you can identify it later:

因此,使用代码:当您创建内存存储时,请指定一个URL,以便您以后可以识别它:

if (![_persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:[NSURL URLWithString:@"memory://0"] options:nil error:&error]) {

In your resetStack method you would remove it after you reset and removed your managed object context(s):

在resetStack方法中,你可以在重置和删除托管对象上下文之后删除它:

[[self persistentStoreCoordinator] removePersistentStore:[[self persistentStoreCoordinator] persistentStoreForURL:[NSURL URLWithString:@"memory://0"] ] error:&error];

If you do not reset and remove the context(s) first, you will start to see exceptions as the objects get faults fired that no longer have a store to go to.

如果您不首先重置和删除上下文,您将开始看到异常,因为对象将触发不再有存储要访问的错误。

You should listen for the notifications NSPersistentStoreCoordinatorStoresDidChangeNotification and NSPersistentStoreCoordinatorStoresWillChangeNotification anywhere you are using Core Data, so that when a store is added or removed you can act accordingly. For example, if you call your resetStack method that removes a store, you should see the NSPersistentStoreCoordinatorStoresWillChangeNotification and use that opportunity to stop whatever you are doing that may be accessing that store. When NSPersistentStoreCoordinatorStoresDidChangeNotification is received, you would complete your tear down of objects or processes that may be using Core Data - like an NSFetchedResultsController that may be attempting to access the store that was just removed. Once the old store has been removed you want to add a new, clean one. When that happens you will get the same notifications, with payloads that indicate a store was added - and at that time you can create new managed object contexts and fetched results controllers to replace the old ones.

您应该侦听NSPersistentStoreCoordinatorStoresDidChangeNotification和NSPersistentStoreCoordinatorStoresWillChangeNotification,它们会在使用核心数据的任何地方进行更改,以便当添加或删除一个存储时,您可以相应地采取行动。例如,如果您调用删除一个存储的resetStack方法,您应该会看到NSPersistentStoreCoordinatorStoresWillChangeNotification并利用这个机会停止任何可能正在访问该存储的操作。当接收到NSPersistentStoreCoordinatorStoresDidChangeNotification时,您将完成对可能使用核心数据的对象或进程的分解——比如NSFetchedResultsController,它可能试图访问刚刚删除的存储。一旦旧的商店被移除,你想添加一个新的,干净的。当发生这种情况时,您将获得相同的通知,并使用指示添加了存储的有效负载——此时,您可以创建新的托管对象上下文并获取结果控制器来替换旧的。

This is all actually a lot simpler than it sounds. Nonetheless, make sure you're doing all of this for the right reasons.

这实际上比听起来要简单得多。尽管如此,要确保你做这些事情的理由是正确的。

#2


3  

As I've noted in my comment:
It is sufficient to set the CoreData stack components to nil

正如我在评论中所指出的:将CoreData堆栈组件设置为nil就足够了。

Assuming a boilerplate stack you could achieve that by:

假设有一个样板堆栈,你可以通过:

In you AppDelegate .m file, make the core components writable:

在您的AppDelegate .m文件中,使核心组件可写:

@interface DSAppDelegate ()
@property (readwrite, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readwrite, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readwrite, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@end

Add a reset function:

添加一个重置功能:

- (void) resetStack
{
    self.managedObjectContext = nil;
    self.persistentStoreCoordinator = nil;
    self.managedObjectModel = nil; //not necessary if store will not change
}

calling resetStack will release memory occupied by the in-memory store (might take some time if the store is heavily populated).

调用resetStack将释放内存存储所占用的内存(如果存储被大量填充,可能需要一些时间)。

Remember not to use any stack component during the reset, or keeping strong references to them as you might not get the stack to reset properly

记住在重置过程中不要使用任何堆栈组件,或者保持对它们的强引用,因为您可能无法使堆栈正确地重置

This is demonstrated in THIS test project.
(In instruments, view memory allocations relative to the console output).

这在这个测试项目中得到了演示。(在工具中,查看与控制台输出相关的内存分配)。

Edit:
@quellish solution is more elegant (removing and adding a new store), and instead of replacing the managed context of the view controller you need only reset it, although you will have to nullify your FRC in any case.

编辑:@quellish解决方案更优雅(删除并添加一个新存储),与替换视图控制器的托管上下文不同,您只需要重置它,尽管在任何情况下您都必须取消FRC。

#3


0  

In case of Core Data In-memory store, the data is persisted until your app is quit or suspended. So you can just leave it to the OS to suspend your app when there are any memory issues in the device.

在内存中存储核心数据时,数据将一直保存到应用程序退出或挂起为止。你可以让操作系统在设备有内存问题时暂停你的应用。

Alternatively you can force the app to quit by calling exit(0), in the applicationDidEnterBackground:(UIApplication *)application method. But this is not recommended.

或者,您可以在applicationDidEnterBackground:(UIApplication *)应用程序方法中通过调用exit(0)来强制应用程序退出。但这是不推荐的。

The safest method would be to manually iterate through your database, deleting all the content.

最安全的方法是手动遍历数据库,删除所有内容。

#1


9  

In the case of an in-memory store remove the store from the NSPersistentStoreCoordinator using removePersistentStore:error:. At that point you can create a new in-memory store attached to the coordinator if you want, which will give you a blank slate at the store level - which seems to be the focus of your question.

在内存中存储的情况下,使用removePersistentStore:error从NSPersistentStoreCoordinator中删除存储。此时,如果您愿意,您可以创建一个附加到协调器的新的内存中存储,这将为您提供一个存储级别的空白记录—这似乎是您的问题的重点。

Note that if you are retaining managed objects that you fetched from an NSManagedObjectContext those may still stick around, and when a fault is fired on them after you have removed your in-memory store they can throw exceptions. It's advisable that if this is likely (usually because you are using a single context, or are not good about using Instruments to track memory use) that you also call reset on your managed object context(s) and remove any lingering strong references to managed objects.

注意,如果您正在保留从NSManagedObjectContext中获取的托管对象,这些对象可能仍然存在,并且当在删除内存存储之后对它们触发错误时,它们可能会抛出异常。如果这是可能的(通常是因为您正在使用一个上下文,或者不善于使用工具来跟踪内存使用),您也可以在托管对象上下文上调用reset,并删除对托管对象的任何持久强引用。

So using your code: When you create the in-memory store, specify a URL so you can identify it later:

因此,使用代码:当您创建内存存储时,请指定一个URL,以便您以后可以识别它:

if (![_persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:[NSURL URLWithString:@"memory://0"] options:nil error:&error]) {

In your resetStack method you would remove it after you reset and removed your managed object context(s):

在resetStack方法中,你可以在重置和删除托管对象上下文之后删除它:

[[self persistentStoreCoordinator] removePersistentStore:[[self persistentStoreCoordinator] persistentStoreForURL:[NSURL URLWithString:@"memory://0"] ] error:&error];

If you do not reset and remove the context(s) first, you will start to see exceptions as the objects get faults fired that no longer have a store to go to.

如果您不首先重置和删除上下文,您将开始看到异常,因为对象将触发不再有存储要访问的错误。

You should listen for the notifications NSPersistentStoreCoordinatorStoresDidChangeNotification and NSPersistentStoreCoordinatorStoresWillChangeNotification anywhere you are using Core Data, so that when a store is added or removed you can act accordingly. For example, if you call your resetStack method that removes a store, you should see the NSPersistentStoreCoordinatorStoresWillChangeNotification and use that opportunity to stop whatever you are doing that may be accessing that store. When NSPersistentStoreCoordinatorStoresDidChangeNotification is received, you would complete your tear down of objects or processes that may be using Core Data - like an NSFetchedResultsController that may be attempting to access the store that was just removed. Once the old store has been removed you want to add a new, clean one. When that happens you will get the same notifications, with payloads that indicate a store was added - and at that time you can create new managed object contexts and fetched results controllers to replace the old ones.

您应该侦听NSPersistentStoreCoordinatorStoresDidChangeNotification和NSPersistentStoreCoordinatorStoresWillChangeNotification,它们会在使用核心数据的任何地方进行更改,以便当添加或删除一个存储时,您可以相应地采取行动。例如,如果您调用删除一个存储的resetStack方法,您应该会看到NSPersistentStoreCoordinatorStoresWillChangeNotification并利用这个机会停止任何可能正在访问该存储的操作。当接收到NSPersistentStoreCoordinatorStoresDidChangeNotification时,您将完成对可能使用核心数据的对象或进程的分解——比如NSFetchedResultsController,它可能试图访问刚刚删除的存储。一旦旧的商店被移除,你想添加一个新的,干净的。当发生这种情况时,您将获得相同的通知,并使用指示添加了存储的有效负载——此时,您可以创建新的托管对象上下文并获取结果控制器来替换旧的。

This is all actually a lot simpler than it sounds. Nonetheless, make sure you're doing all of this for the right reasons.

这实际上比听起来要简单得多。尽管如此,要确保你做这些事情的理由是正确的。

#2


3  

As I've noted in my comment:
It is sufficient to set the CoreData stack components to nil

正如我在评论中所指出的:将CoreData堆栈组件设置为nil就足够了。

Assuming a boilerplate stack you could achieve that by:

假设有一个样板堆栈,你可以通过:

In you AppDelegate .m file, make the core components writable:

在您的AppDelegate .m文件中,使核心组件可写:

@interface DSAppDelegate ()
@property (readwrite, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readwrite, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readwrite, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@end

Add a reset function:

添加一个重置功能:

- (void) resetStack
{
    self.managedObjectContext = nil;
    self.persistentStoreCoordinator = nil;
    self.managedObjectModel = nil; //not necessary if store will not change
}

calling resetStack will release memory occupied by the in-memory store (might take some time if the store is heavily populated).

调用resetStack将释放内存存储所占用的内存(如果存储被大量填充,可能需要一些时间)。

Remember not to use any stack component during the reset, or keeping strong references to them as you might not get the stack to reset properly

记住在重置过程中不要使用任何堆栈组件,或者保持对它们的强引用,因为您可能无法使堆栈正确地重置

This is demonstrated in THIS test project.
(In instruments, view memory allocations relative to the console output).

这在这个测试项目中得到了演示。(在工具中,查看与控制台输出相关的内存分配)。

Edit:
@quellish solution is more elegant (removing and adding a new store), and instead of replacing the managed context of the view controller you need only reset it, although you will have to nullify your FRC in any case.

编辑:@quellish解决方案更优雅(删除并添加一个新存储),与替换视图控制器的托管上下文不同,您只需要重置它,尽管在任何情况下您都必须取消FRC。

#3


0  

In case of Core Data In-memory store, the data is persisted until your app is quit or suspended. So you can just leave it to the OS to suspend your app when there are any memory issues in the device.

在内存中存储核心数据时,数据将一直保存到应用程序退出或挂起为止。你可以让操作系统在设备有内存问题时暂停你的应用。

Alternatively you can force the app to quit by calling exit(0), in the applicationDidEnterBackground:(UIApplication *)application method. But this is not recommended.

或者,您可以在applicationDidEnterBackground:(UIApplication *)应用程序方法中通过调用exit(0)来强制应用程序退出。但这是不推荐的。

The safest method would be to manually iterate through your database, deleting all the content.

最安全的方法是手动遍历数据库,删除所有内容。