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

时间:2015-08-13 13:28:10

标签: ios core-data ios8

我有一个CoreData配置,我存储了测量数据。数据按Session分组,因此每个Session实体与SensorData具有一对多关系。删除规则设置为Cascade。我还有一个全部删除按钮。按下它时,我运行以下代码。

// 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.");

我会得到如下所示的日志输出,因此执行速度相当快。然后用户界面冻结和RAM使用火箭。 有大约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不变(如建议的那样)。但是,如果删除SensorData中的200,则保存大约需要3秒,但前1000个条目除外。删除速度很快。见下面的追踪。

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

Trace 微量

3 个答案:

答案 0 :(得分:2)

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

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

尝试像这样加载例如100乘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];
} 

答案 1 :(得分:1)

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

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

接收器的批量大小。

     

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

     

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

     

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

     

出于线程安全的考虑,您应该考虑执行fetch时执行的数组代理是由执行请求的托管对象上下文所拥有的,并将其视为使用该上下文注册的托管对象。

答案 2 :(得分:1)

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

iOS9引入了NSBatchDeleteRequest,它可以为您提供帮助。您可以在此处查看WWDC视频:https://developer.apple.com/videos/wwdc/2015/?id=220,并在视频开始15分钟后讨论该部分。

以下是类似问题/答案的链接:Core Data: delete all objects of an entity type, ie clear a table