我的核心数据存储包含51个Entry
个实体,其message
属性读取为“Test”。为此执行NSFetchRequest确认它们都在那里。
然而,我的方法的另一部分将用于内存密集型目的,处理大量的NSData,因此我需要经常调用[oldContext reset];
。
我有一个内存密集型方法,可以从我的MOC访问很多NSData。因此,它会定期调用[oldContext reset];
。没有这一行,就会耗尽内存。
我发现通过使用它,但它没有返回正确的结果。为了测试这一点,我注释掉了数据密集型代码,给我留下了返回message
属性的代码,其中51个属性设置为“Test”(由单独的NSFetchRequest确认)。
然而,使用[oldContext reset];
,它只返回6个结果,并将消息设置为“Test”。这是我正在使用的代码:
NSFetchRequest *oldFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *oldEntryEntity = [NSEntityDescription entityForName:@"Entry"
inManagedObjectContext:oldContext];
[oldFetchRequest setEntity:oldEntryEntity];
[oldFetchRequest setFetchBatchSize:10];
[oldFetchRequest setIncludesPropertyValues:NO];
NSArray *entrys = [oldContext executeFetchRequest:oldFetchRequest error:&error];
int totalEntries = [oldContext countForFetchRequest:oldFetchRequest error:nil];
int i = 0;
while (i < totalEntries) {
@autoreleasepool {
Entry *entry = [entrys objectAtIndex:i];
NSLog(@"message 1: %@", [entry valueForKey:@"message"]);
[oldContext reset];
i++;
}
}
有什么想法,为什么它没有给出51“测试”结果它应该做什么?
答案 0 :(得分:1)
尝试将获取批量大小设置为1而不是当前10。
我对这种情况的解释如下。
executeFetchRequest...
,它会将10条记录提取到上下文中。51 % 10 = 6
条记录仍然存在。答案 1 :(得分:1)
NSFetchRequest *oldFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *oldEntryEntity =
[NSEntityDescription entityForName:@"Entry"
inManagedObjectContext:oldContext];
[oldFetchRequest setEntity:oldEntryEntity];
[oldFetchRequest setFetchBatchSize:10];
[oldFetchRequest setIncludesPropertyValues:NO];
NSArray *entrys = [oldContext executeFetchRequest:oldFetchRequest error:&error];
int totalEntries = [oldContext countForFetchRequest:oldFetchRequest error:nil];
上述代码在MOC oldContext
上执行获取请求。因此,entrys
数组将包含数据库中与请求匹配的每个对象的托管对象。此外,作为设置批量大小的结果,获取将使批量大小的对象出错。
作为确保自己发生了什么的实验,请添加这些日志语句......
NSLog(@"entrys count = %u", entrys.count);
for (NSManagedObject *entry in entrys) {
NSLog(@"entry: %@", entry);
}
你看到那个数组中有什么?现在有意义吗?
让我们看一下代码的其余部分。
int i = 0;
while (i < totalEntries) {
@autoreleasepool {
// You get the i-th entry. It will be a managed object. It could be a fault
// or it could be a fully hydrated object. Based on your batch size, the
// first ten (0 <= i < 10) will be complete objects.
Entry *entry = [entrys objectAtIndex:i];
// Log the "message" attribute. By calling valueForKey, the object will be
// faulted into memory if it is a fault. Since your batch size is 10,
// this will make sure 10 objects are faulted if one is needed.
NSLog(@"message 1: %@", [entry valueForKey:@"message"]);
// Resetting the entire context blows away everything in the context.
// Calling reset is a hard call, and should not be done if you have
// references to the objects in the context.
[oldContext reset];
i++;
}
}
我会建议一种不同的方法。调用reset
不适用于保留对象的上下文。
有几种选择。您可以创建子上下文,在那里执行临时工作,然后释放上下文。它将释放它使用的所有内存。
您可以有选择地使用
- (void)refreshObject:(NSManagedObject *)object mergeChanges:(BOOL)flag
如果flag
为NO
,会将对象重新变为故障,从而释放其内存。请注意,这也有一些固有的危险,特别是如果你有管理关系。
还有其他一些选择,但不知道你的最终目标是什么......有点难以分辨哪些是最有益的。
我强烈建议您阅读与这些电话相关的所有文档。事实上,虽然Core Data非常复杂,但它确实有一些“脆弱”的交互操作,如果你做任何非平凡的事情,你应该知道。
我强烈建议您阅读所有Core Data文档。它解决了您在项目中遇到的所有问题。
答案 2 :(得分:1)
根据Apple文档,这是ManagedObjectContext重置的功能:
- 所有接收者的管理对象都被“遗忘”。如果您使用此方法,您应该确保您也放弃对使用接收器获取的任何管理对象的引用,因为它们之后将无效。
所以在你的代码中,你告诉oldContext每次while循环迭代都会忘记它的所有对象。
你没有说明在MOC中NSData的来源,但是如果它来自Entry实体,我会说你有两个选择:
1)不要每次都重置MOC,而是使用NSManagedObjectContext的refreshObject:mergeChanges:
方法。这将重新排除实体故障并释放内存。例如:
while (i < totalEntries) {
@autoreleasepool {
Entry *entry = [entrys objectAtIndex:i];
NSLog(@"message 1: %@", [entry valueForKey:@"message"]);
[oldContext refreshObject:entry mergeChanges:NO];
i++;
}
}
2)不要在Core Data实体中存储大量数据。而是将数据写入文件系统并保持对实体中数据路径的引用。这将是我的首选方法。请查看“大数据对象”部分下的Apple Core Data Performance