CoreData - 删除所有实体消耗 RAM 并需要很长时间
CoreData - delete all entities consumes RAM and take a long time
我有一个存储测量数据的 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 个会话和大约 100 万个测量值(每个测量值 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(虽然可能是一个很好的)。
追踪
在不知道上述问题答案的情况下快速解决。我会先删除所有测量数据,然后再删除会话数据。但最重要的是,您应该始终在获取请求时设置批量大小。
// Fetch only managedObjectID to reduce memory impact
NSFetchRequest *sessionsRequest = [NSFetchRequest fetchRequestWithEntityName:@"Session"];
[sessionsRequest setIncludesPropertyValues:NO];
[sessionsRequest setFetchBatchSize:20];
The batch size of the receiver.
The default value is 0. A batch size of 0 is treated as infinite, which disables the batch faulting behavior.
If you set a non-zero batch size, the collection of objects returned when the fetch is executed is broken into batches. When the fetch is executed, the entire request is evaluated and the identities of all matching objects recorded, but no more than batchSize objects’ data will be fetched from the persistent store at a time. The array returned from executing the request will be a proxy object that transparently faults batches on demand. (In database terms, this is an in-memory cursor.)
You can use this feature to restrict the working set of data in your application. In combination with fetchLimit, you can create a subrange of an arbitrary result set.
For purposes of thread safety, you should consider the array proxy returned when the fetch is executed as being owned by the managed object context the request is executed against, and treat it as if it were a managed object registered with that context.
占用大量内存的是您正在加载所有会话:
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 视频:https://developer.apple.com/videos/wwdc/2015/?id=220,该部分从视频开始的 15 分钟开始讨论。
这是一个 link 到类似的 question/answer:Core Data: delete all objects of an entity type, ie clear a table
我有一个存储测量数据的 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 个会话和大约 100 万个测量值(每个测量值 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(虽然可能是一个很好的)。
在不知道上述问题答案的情况下快速解决。我会先删除所有测量数据,然后再删除会话数据。但最重要的是,您应该始终在获取请求时设置批量大小。
// Fetch only managedObjectID to reduce memory impact
NSFetchRequest *sessionsRequest = [NSFetchRequest fetchRequestWithEntityName:@"Session"];
[sessionsRequest setIncludesPropertyValues:NO];
[sessionsRequest setFetchBatchSize:20];
The batch size of the receiver.
The default value is 0. A batch size of 0 is treated as infinite, which disables the batch faulting behavior.
If you set a non-zero batch size, the collection of objects returned when the fetch is executed is broken into batches. When the fetch is executed, the entire request is evaluated and the identities of all matching objects recorded, but no more than batchSize objects’ data will be fetched from the persistent store at a time. The array returned from executing the request will be a proxy object that transparently faults batches on demand. (In database terms, this is an in-memory cursor.)
You can use this feature to restrict the working set of data in your application. In combination with fetchLimit, you can create a subrange of an arbitrary result set.
For purposes of thread safety, you should consider the array proxy returned when the fetch is executed as being owned by the managed object context the request is executed against, and treat it as if it were a managed object registered with that context.
占用大量内存的是您正在加载所有会话:
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 视频:https://developer.apple.com/videos/wwdc/2015/?id=220,该部分从视频开始的 15 分钟开始讨论。
这是一个 link 到类似的 question/answer:Core Data: delete all objects of an entity type, ie clear a table