CoreData - 删除所有实体消耗 RAM 并需要很长时间
Posted
技术标签:
【中文标题】CoreData - 删除所有实体消耗 RAM 并需要很长时间【英文标题】:CoreData - delete all entities consumes RAM and take a long time 【发布时间】:2015-08-13 13:28:10 【问题描述】:我有一个存储测量数据的 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.");
发生的情况是我得到了如下的日志输出,因此执行得相当快。但随后 UI 冻结,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 个条目。不过删除速度很快。请参阅下面的跟踪。
我想在不删除文档的情况下解决此问题。感觉就像一个 hack(尽管可能是一个很好的)。
追踪
【问题讨论】:
你是如何设置你的 CoreData 堆栈的?你在使用 UIManagedDocument 吗? self.context 是主线程 NSManagedObjectContext 吗?您的操作系统部署目标版本是什么? 看起来您使用的是UIDocument
,在这种情况下,删除当前文档可能更容易。
是的,我正在使用 UIDocument。 self.context
是 UIDocument 的 NSManagedObjectContext (即部署目标是 ios8(在标签中模糊地指定)。我还尝试将 performBlock
更改为 performBlockAndWait
,执行速度很快。
【参考方案1】:
需要大量内存的是您正在加载所有会话:
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];
【讨论】:
这不能直接工作,因为大部分数据不包含在Session
中,而是在SensorData
中。因此,即使我将限制设置为 1,它仍然可以获取 100k SensorData
。但是如果我用这种方法先直接删除SensorData
,然后删除Session
,效果很好。但是,删除 200 个SensorData
后,保存最多可能需要 3 秒。任何线索为什么?【参考方案2】:
在不知道上述问题答案的情况下快速解决。我会先删除所有测量数据,然后删除会话数据。但最重要的是,您应该始终在 fetch 请求上设置批量大小。
// Fetch only managedObjectID to reduce memory impact
NSFetchRequest *sessionsRequest = [NSFetchRequest fetchRequestWithEntityName:@"Session"];
[sessionsRequest setIncludesPropertyValues:NO];
[sessionsRequest setFetchBatchSize:20];
接收器的批量大小。
默认值为 0。批量大小为 0 被视为无限,这将禁用批量错误行为。
如果您设置非零批量大小,则执行提取时返回的对象集合将分成多个批次。执行 fetch 时,会评估整个请求并记录所有匹配对象的身份,但一次从持久存储中获取不超过 batchSize 对象的数据。执行请求返回的数组将是一个代理对象,可以根据需要透明地对批次进行故障处理。 (在数据库术语中,这是一个内存游标。)
您可以使用此功能来限制应用程序中的工作数据集。结合 fetchLimit,您可以创建任意结果集的子范围。
出于线程安全的目的,您应该将执行 fetch 时返回的数组代理视为由执行请求的托管对象上下文所拥有,并将其视为在该上下文中注册的托管对象。
【讨论】:
【参考方案3】:正如 Tom 所说,删除整个文档可能更容易,然后从新文档开始。
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
【讨论】:
以上是关于CoreData - 删除所有实体消耗 RAM 并需要很长时间的主要内容,如果未能解决你的问题,请参考以下文章
从 TableView 中的 CoreData 中删除实体时出错