【问题标题】:Core Data memory issue核心数据内存问题
【发布时间】:2012-02-12 23:28:27
【问题描述】:

我将数千个数据插入到我的 SQLite 存储的核心数据中。 我的 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];

    }

最后我为 ManagedObjectContext 调用了重置。

[[self managedObjectContext] reset];

但问题是这个过程(插入)占用的内存在完成后没有取回。

任何人都可以建议我这有什么问题,或者这是预期的行为? 有什么办法可以避免这种内存泄漏?

更新

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

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

【问题讨论】:

    标签: iphone objective-c ios core-data memory-management


    【解决方案1】:

    我们在项目中遇到了同样的问题。重置上下文几乎没有释放任何内存。结果是在所有父上下文上完成重置后问题已得到解决:

    NSManagedObjectContext *currentContext = context;
    
    while (currentContext) {
    
        [currentContext reset];
        currentContext = [currentContext parentContext];
    }
    

    【讨论】:

    • 这很有魅力,我只是为上下文调用重置,而父上下文仍然持有内存。非常感谢。
    【解决方案2】:

    这是自动释放池问题的主题。没有 authorelease 应用程序会保留一些时间(只有苹果人知道,多少时间。如果你想把进程掌握在你手中,只需更改你的代码:

    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;
    

    【讨论】:

    • 它不会放回使用过的内存
    • 在mac中使用活动监视器
    • 看起来 Galazas0 给出了我的想法,并且表现得更好:) 无论如何,这一定有帮助
    【解决方案3】:

    您的代码缺少自动释放池:D 这意味着您在内存中存储了 1000 个 MyEntity 对象,这将导致崩溃并且 iOS 将 FC 您的应用程序(强制关闭)。代码应如下所示:

    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];
    

    希望这对您有所帮助,不会让您感到困惑!

    【讨论】:

    • 我尝试了 NSAutoreleasePool 但它没有给我使用的内存。我找不到任何 [NSEntityDescription objectForEntityForName:@"MyEntity"] 方法。
    • 您可以做的其他事情是直接对上下文中的对象进行操作,所以让我编辑我的代码以向您展示如何做到这一点。
    • 如果您认为此答案对您有所帮助,请将其标记为正确! :]
    • 对不起 Galaxas 它不工作,[[NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:managedObjectContext] name] = [NSString stringWithFormat:@"%d", i];这将给出错误“分配给不允许的objective-c消息的'readonly'返回结果”,这将 [[NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:managedObjectContext] setName: [NSString stringWithFormat:@"%d", i ]];导致崩溃也..
    • 您是否正确设置了它的属性?它应该是 @property(nonatomic, retain) id age;// 或 id name;并将 id 正确替换为变量的类型。
    【解决方案4】:

    虽然有点晚了,

    我发现将undo 设置为nil 没有帮助,但以下确实防止了核心数据的泄漏。

    [[self managedObjectContext] processPendingChanges];  // flush operations
    [[[self managedObjectContext] undoManager] disableUndoRegistration]; // disable undo
    

    关于自动释放池。遇到异常(包括 Numberformat 异常)object.name = [NSString stringWithFormat:@"%d", i]; 您的池不会按预期耗尽。事实上,另一个自动释放池将耗尽所有自动释放的实例。内存会泄漏,例如通过自动释放创建的异常。所以建议如下。

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

    我建议不要放入自动释放池,弄清楚[[[self managedObjectContext] undoManager] disableUndoRegistration]; 是否有助于较小的集合,然后再移动更大。核心数据中的撤消管理器是一艘漏水的船,我还没有找到解决方案。

    【讨论】:

      猜你喜欢
      • 2014-07-24
      • 2014-05-07
      • 2010-11-26
      • 2015-05-20
      • 2011-07-03
      相关资源
      最近更新 更多