NSManagedObjectContext重置似乎丢失了数据?

时间:2012-09-26 12:17:22

标签: iphone objective-c ios core-data managedobjectcontext

我的核心数据存储包含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“测试”结果它应该做什么?

3 个答案:

答案 0 :(得分:1)

尝试将获取批量大小设置为1而不是当前10。

我对这种情况的解释如下。

  1. executeFetchRequest...,它会将10条记录提取到上下文中。
  2. 但是,通过重置上下文,所有记录都将丢失。所以,只有10个计数中的第一个记录。请注意,在上下文重置之前记录第一条记录。
  3. 由于我们共有51条记录,因此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

如果flagNO,会将对象重新变为故障,从而释放其内存。请注意,这也有一些固有的危险,特别是如果你有管理关系。

还有其他一些选择,但不知道你的最终目标是什么......有点难以分辨哪些是最有益的。

我强烈建议您阅读与这些电话相关的所有文档。事实上,虽然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