Ios CoreData-删除所有实体会消耗RAM并花费很长时间

Ios CoreData-删除所有实体会消耗RAM并花费很长时间,ios,core-data,ios8,Ios,Core Data,Ios8,我有一个CoreData配置,存储测量数据。数据按会话分组,因此每个会话实体与传感器数据具有一对多关系。删除规则设置为级联。我还有一个删除所有按钮。当按下时,我运行以下代码 // All sessions (cascading) [self.context performBlockAndWait:^{ // Fetch only managedObjectID to reduce memory impact NSFetchRequest *sessionsRequest = [

我有一个CoreData配置,存储测量数据。数据按
会话
分组,因此每个
会话
实体与
传感器数据
具有一对多关系。删除规则设置为级联。我还有一个删除所有按钮。当按下时,我运行以下代码

// All sessions (cascading)
[self.context performBlockAndWait:^{

    // Fetch only managedObjectID to reduce memory impact
    NSFetchRequest *sessionsRequest = [NSFetchRequest fetchRequestWithEntityName:@"Session"];
    [sessionsRequest setIncludesPropertyValues:NO];

    NSError *sessionsError;
    NSArray *allSessions = [self.context executeFetchRequest:sessionsRequest error:&sessionsError];

    if (sessionsError) {
        NSLog(@"Failed to fetch all sessions. %@", sessionsError);
    }

    // Release fetch request
    sessionsRequest = nil;

    // Loop and delete
    int i = 0;
    for (NSManagedObject *session in allSessions) {
        NSLog(@"Deleting session (%d / %d)", ++i, allSessions.count);
        if (i != allSessions.count) {
            [self.context deleteObject:session];
        }
    }


    NSLog(@"All session deleted.");
}];

NSLog(@"Saving.");
[self.context performBlock:^{
    [self.document saveToURL:self.documentPath
        forSaveOperation:UIDocumentSaveForOverwriting
       completionHandler:^(BOOL success){
           NSLog(@"Document saved %@.", success ? @"successfully" : @"unsuccessfully");
       }];
}];
NSLog(@"Done saving.");
我得到的日志输出如下所示,因此执行速度相当快。但是用户界面会冻结,内存使用率也会提高。 共有约60个会话和约1M个测量值(每个测量值有3个浮点值),最终RAM使用量过大,应用程序在大约20分钟后崩溃,所有条目仍然存在

很明显,储蓄在这里起作用,但我做错了什么?谢谢你的指点


日志输出:

2015-08-13 15:16:56.825 MyApp[4201:1660697] Deleting session (1 / 61)
2015-08-13 15:16:56.826 MyApp[4201:1660697] Deleting session (2 / 61)
.
.
.
2015-08-13 15:16:56.862 MyApp[4201:1660697] Deleting session (60 / 61)
2015-08-13 15:16:56.863 MyApp[4201:1660697] Deleting session (61 / 61)
2015-08-13 15:16:56.863 MyApp[4201:1660697] All session deleted.
2015-08-13 15:16:56.864 MyApp[4201:1660697] Saving.

2015-08-13 15:16:56.864 MyApp[4201:1660697] Done saving.

更新

我编辑了这个过程,首先删除了测量数据,并设置了一个取数限制来降低RAM(如建议的那样)。但是,如果我删除200个
传感器数据
,则除前1000个条目外,保存大约需要3秒钟。不过删除很快。请参阅下面的跟踪

我想在不删除文档的情况下修复此问题。感觉像一个黑客(虽然可能是一个好的)


追踪

作为一种快速修复方法,而不知道上述问题的答案。我会先删除所有测量数据,然后删除会话数据。但最重要的是,您应该始终在获取请求上设置批大小

// Fetch only managedObjectID to reduce memory impact
NSFetchRequest *sessionsRequest = [NSFetchRequest fetchRequestWithEntityName:@"Session"];
[sessionsRequest setIncludesPropertyValues:NO];
[sessionsRequest setFetchBatchSize:20];
接收器的批大小

默认值为0。0的批大小被视为无限,这将禁用批错误行为

如果将批大小设置为非零,则在执行提取时返回的对象集合将分为多个批。执行提取时,将评估整个请求并记录所有匹配对象的标识,但每次从持久存储中提取的数据不超过batchSize对象的数据。执行请求返回的数组将是一个代理对象,它会根据需要透明地对批处理进行故障处理。(在数据库术语中,这是内存中的游标。)

您可以使用此功能限制应用程序中的工作数据集。结合fetchLimit,您可以创建任意结果集的子范围

出于线程安全的目的,您应该考虑在执行执行时将返回的数组代理归为被请求的对象对象所拥有的上下文,并将其视为是与该上下文注册的托管对象。


在不知道上述问题答案的情况下快速修复。我会先删除所有测量数据,然后删除会话数据。但最重要的是,您应该始终在获取请求上设置批大小

// Fetch only managedObjectID to reduce memory impact
NSFetchRequest *sessionsRequest = [NSFetchRequest fetchRequestWithEntityName:@"Session"];
[sessionsRequest setIncludesPropertyValues:NO];
[sessionsRequest setFetchBatchSize:20];
接收器的批大小

默认值为0。0的批大小被视为无限,这将禁用批错误行为

如果将批大小设置为非零,则在执行提取时返回的对象集合将分为多个批。执行提取时,将评估整个请求并记录所有匹配对象的标识,但每次从持久存储中提取的数据不超过batchSize对象的数据。执行请求返回的数组将是一个代理对象,它会根据需要透明地对批处理进行故障处理。(在数据库术语中,这是内存中的游标。)

您可以使用此功能限制应用程序中的工作数据集。结合fetchLimit,您可以创建任意结果集的子范围

出于线程安全的目的,您应该考虑在执行执行时将返回的数组代理归为被请求的对象对象所拥有的上下文,并将其视为是与该上下文注册的托管对象。


占用大量内存的是,您正在加载所有会话:

NSArray *allSessions = [self.context executeFetchRequest:sessionsRequest error:&sessionsError];
尝试按如下方式加载例如100 x 100:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[self.context setUndoManager:nil];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Session" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesPropertyValues:NO];
[fetchRequest setFetchLimit:100];
NSError *error;
NSArray *items = [self.context executeFetchRequest:fetchRequest error:&error];
while ([items count] > 0) {
    @autoreleasepool {
        for (NSManagedObject *item in items) {
            [self.context deleteObject:item];
        }
        if (![self.context save:&error]) {
            NSLog(@"Error deleting %@ - error:%@",self.entityName, error);
        }
    }
    items = [self.context executeFetchRequest:fetchRequest error:&error];
} 

占用大量内存的是,您正在加载所有会话:

NSArray *allSessions = [self.context executeFetchRequest:sessionsRequest error:&sessionsError];
尝试按如下方式加载例如100 x 100:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[self.context setUndoManager:nil];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Session" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesPropertyValues:NO];
[fetchRequest setFetchLimit:100];
NSError *error;
NSArray *items = [self.context executeFetchRequest:fetchRequest error:&error];
while ([items count] > 0) {
    @autoreleasepool {
        for (NSManagedObject *item in items) {
            [self.context deleteObject:item];
        }
        if (![self.context save:&error]) {
            NSLog(@"Error deleting %@ - error:%@",self.entityName, error);
        }
    }
    items = [self.context executeFetchRequest:fetchRequest error:&error];
} 

正如Tom所说,删除整个文档并从新文档开始可能更容易

iOS9引入了
NSBatchDeleteRequest
,这将对您有所帮助。您可以在此处看到WWDC视频:,该部分将在视频开始15分钟后讨论


这里有一个类似问题/答案的链接:

正如Tom所说,删除整个文档并从新文档开始可能更容易

iOS9引入了
NSBatchDeleteRequest
,这将对您有所帮助。您可以在此处看到WWDC视频:,该部分将在视频开始15分钟后讨论


这里有一个类似问题/答案的链接:

您如何设置CoreData堆栈?您正在使用UIManagedDocument吗?self.context是主线程NSManagedObjectContext吗?您的操作系统部署目标版本是什么?看起来您正在使用
UIDocument
,在这种情况下,删除当前文档可能更容易。是的,我正在使用UIDocument
self.context
是UIDocument的NSManagedObjectContext(即部署目标是iOS8(如标签中模糊指定的)。我还尝试将
performBlock
更改为
performBlockAndWait
,执行速度很快。您如何设置CoreData堆栈?是否使用UIManagedDocument?self.context是主线程NSManagedObjectContext吗?您的操作系统部署目标版本是什么?看起来您正在使用
UIDocument
,其中案例删除当前文档可能更容易。是的,我正在使用UIDocument。
self.context
是UIDocument的NSManagedObjectContext(即部署目标是iOS8(如标签中模糊指定的)。我还尝试更改
performBlock