核心数据内存问题

3

我正在将数千条数据插入到我的SQLite存储的Core data中。 我的ManagedObjectContext的UndoManager设置为nil。 我在for循环中插入NSManagedObject。 尝试以两种方式保存上下文,

1 循环完成后(在for循环外)保存。

for(int i =0;i<1000;i++){

        MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

        object.name = [NSString stringWithFormat:@"%d",i];
        object.age = [NSNumber numberWithInt:i];


    }

    [self managedObjectContext] save:nil];

每次插入都需要2个(在for循环内部)。

for(int i =0;i<1000;i++){

        MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

        object.name = [NSString stringWithFormat:@"%d",i];
        object.age = [NSNumber numberWithInt:i];
        [self managedObjectContext] save:nil];

    }

最后,我调用了Managed Context的reset方法。

[[self managedObjectContext] reset];

但问题在于完成插入操作后,为此进程所占用的内存没有得到释放。

有人能告诉我这是什么问题,或者这是预期的行为吗? 有没有避免这种内存泄漏的解决方法?

更新

我尝试了[[self managedObjectContext] refreshObject:object mergeChanges:NO];它可以释放一部分内存,但并不能完全释放。我仍在寻找一个管理大量数据在CoreData中的好解决方案。

如果有人能向我建议一个处理CoreData中大量数据的示例项目(源代码),那将对我很有帮助。 谢谢。

4个回答

2

我们在项目中遇到了同样的问题。重置上下文几乎没有释放任何内存。事实证明,当所有父上下文都进行重置时,问题得以解决:

NSManagedObjectContext *currentContext = context;

while (currentContext) {

    [currentContext reset];
    currentContext = [currentContext parentContext];
}

这个代码运行得非常好,我只是调用了“reset”来重置上下文,但父级上下文仍然保留了内存。 非常感谢。 - Pavan

1
这是关于自动释放池问题的主题。如果没有自动释放,应用程序将在一段时间内保留内存(只有苹果的人知道多长时间)。如果你想亲自控制进程,只需更改你的代码:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(int i =0;i<1000;i++){

        MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

        object.name = [NSString stringWithFormat:@"%d",i];
        object.age = [NSNumber numberWithInt:i];
        [self managedObjectContext] save:nil];
        [pool drain], pool = nil;
        pool =  [[NSAutoreleasePool alloc] init];
    }
 [pool drain], pool = nil;

它不会释放已使用的内存。 - Raj
在Mac中使用活动监视器 - Raj
1
看起来 Galazas0 更好地表达了我的想法 :) 无论如何,那一定会有所帮助。 - user170317

1

你的代码缺少自动释放池 :D 这意味着你正在将1000个MyEntity对象存储在内存中,这将导致崩溃并且iOS将强制关闭你的应用程序。代码应该像这样:

for(int i = 0; i < 1000; i++) {
        @autoreleasepool {
                MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

                object.name = [NSString stringWithFormat:@"%d", i];
                object.age = [NSNumber numberWithInt:i];
        }
    }
    [self managedObjectContext] save:nil];

或者:

for(int i = 0; i < 1000; i++) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

        object.name = [NSString stringWithFormat:@"%d", i];
        object.age = [NSNumber numberWithInt:i];
        [pool drain];
    }
    [self managedObjectContext] save:nil];

此外,这可能不是处理这种事情的最佳方法,我建议将其添加到上下文中,然后对上下文本身进行操作,例如使用

[[NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:managedObjectContext] name] = [NSString stringWithFormat:@"%d", i];

或者类似的方式。这样你就不会创建对象的本地副本并堵塞内存。

使用三种方法中的最后一种,我会这样做:

for(int i = 0; i < 1000; i++) {
        [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

        [[NSEntityDescription entityForName:@"MyEntity"  inManagedObjectContext:managedObjectContext] name] = [NSString stringWithFormat:@"%d", i];
        [[NSEntityDescription entityForName:@"MyEntity"  inManagedObjectContext:managedObjectContext] age] = [NSNumber numberWithInt:i];
    }
    [self managedObjectContext] save:nil];

如果这个MyEntity是你自己的自定义类,你甚至可以通过使用它来简化你的代码

@property(nonatomic, retain) id name;
@property(nonatomic, retain) id age;

用正确的变量和对象类替换id nameid age,这样你的代码会更简单:

for(int i = 0; i < 1000; i++) {
        [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

        [[NSEntityDescription entityForName:@"MyEntity"  inManagedObjectContext:managedObjectContext] setName: [NSString stringWithFormat:@"%d", i]];
        [[NSEntityDescription entityForName:@"MyEntity"  inManagedObjectContext:managedObjectContext] setAge: [NSNumber numberWithInt:i]];
    }
    [self managedObjectContext] save:nil];

希望这能帮到你,而不会让你感到困惑!

如果您认为这个答案对您有帮助,请将其标记为正确!:] - Aditya Vaidyam
但是对于我的问题仍然没有合适的答案。我尝试了NSAutoreleasePool,但它无法完全释放内存。 - Raj
代码对读者来说很复杂,因为它包含了许多关系(该部分有超过40行的代码),如果我发布那段代码,我的问题将会被投票下降,非常抱歉。 - Raj
好的,如果你还有问题,你需要更具体地描述,但是我恐怕不能再帮助你了,因为我已经尽可能地诊断了这个问题。 - Aditya Vaidyam
感谢您的合作!:] - Aditya Vaidyam
显示剩余10条评论

1
虽然有点晚了,但我发现将undo设置为nil并没有帮助,但以下方法可以防止核心数据泄漏。
[[self managedObjectContext] processPendingChanges];  // flush operations
[[[self managedObjectContext] undoManager] disableUndoRegistration]; // disable undo

关于autoreleasepool。如果出现异常(包括Numberformat异常),object.name = [NSString stringWithFormat:@"%d", i];您的池将无法按预期排干。实际上,另一个autoreleasepool将排干所有自动释放的实例。内存将泄漏,例如通过autorelease创建的异常。因此建议采用以下方法。
NSException *myexception = nil;

try {
   .... [yadda yadda];
} catch (NSException e)
{
   myexception = [e retain];
   @throw;    
}
finally {
    [pool drain];
    [myexception release];
}

我建议不要加入autorelease池,先确定[[[self managedObjectContext] undoManager] disableUndoRegistration];在较小的数据集上是否有帮助,然后再逐步扩大。在Core Data中,撤销管理器存在内存泄漏问题,我还没有找到解决方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接