1

我的核心数据存储包含 51 个Entry实体,它们的message属性为“测试”。为此执行 NSFetchRequest 确认它们都在那里。

然而,我的方法的另一部分将用于内存密集型目的,处理大块 NSData,因此我需要[oldContext reset];经常调用。

我有一个内存密集型方法,可以从我的 MOC 访问大量 NSData。因此,它会定期调用[oldContext reset];. 没有这条线,它就会耗尽内存。

我发现通过使用它,它并没有返回正确的结果。为了测试这一点,我注释掉了数据密集型代码,留下了返回message属性的代码,其中 51 个设置为“测试”(由单独的 NSFetchRequest 确认)。

但是,使用[oldContext reset];它只返回 6 个结果,消息设置为“测试”。这是我正在使用的代码:

    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 个“测试”结果的任何想法?

4

3 回答 3

1

根据 Apple 文档,这是 ManagedObjectContext reset 所做的:

  • 所有接收者管理的对象都被“遗忘”了。如果使用此方法,则应确保还丢弃对使用接收器获取的任何托管对象的引用,因为之后它们将无效。

因此,在您的代码中,您告诉 oldContext 在每次 while 循环迭代时忘记它的所有对象。

您没有说 NSData 来自 MOC 中的哪个位置,但如果它来自 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) 不要在核心数据实体中存储大量数据。相反,将数据写入文件系统并保留对实体中数据路径的引用。那将是我的首选方法。在“大数据对象”部分查看 Apple 的核心数据性能

于 2012-09-26T17:50:47.917 回答
1

尝试将 fetch batch size 设置为 1 而不是当前的 10。

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

  1. 通过executeFetchRequest...它将 10 条记录提取到上下文中。
  2. 但是,通过重置上下文,所有记录都将丢失。所以,只有 10 条记录中的第一个记录。请注意,在上下文重置之前记录第一条记录。
  3. 由于我们总共有 51 条记录,所以51 % 10 = 6记录仍然存在。
于 2012-09-26T12:33:11.950 回答
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 上执行一个 fetch 请求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 非常复杂,但它确实有一些“脆弱”的交互作用,如果你做任何不重要的事情,你应该知道这些。

我强烈建议阅读所有核心数据文档。它解决了您在项目中遇到的所有问题。

于 2012-09-26T13:43:33.240 回答