Ios 导入大型数据集时的核心数据内存使用情况

Ios 导入大型数据集时的核心数据内存使用情况,ios,core-data,memory,Ios,Core Data,Memory,我现在被一个严重的核心数据问题困扰了大约两周。我读了很多博客、文章等问题/答案,但我仍然无法解决我的问题 我运行了很多测试,能够将较大的问题减少到较小的问题。 这将是一个很大的解释,所以请跟我来 问题-数据模型 我必须得到以下数据模型: 对象A与对象B之间存在一对多关系,而对象B与对象C之间存在另一对多关系。由于核心数据建议,我必须创建反向关系,以便B的每个实例指向其父对象A,而C的每个实例指向其父对象B A <->> B <->> C 要释放内存占用空间,

我现在被一个严重的核心数据问题困扰了大约两周。我读了很多博客、文章等问题/答案,但我仍然无法解决我的问题

我运行了很多测试,能够将较大的问题减少到较小的问题。 这将是一个很大的解释,所以请跟我来

问题-数据模型 我必须得到以下数据模型:

对象A与对象B之间存在一对多关系,而对象B与对象C之间存在另一对多关系。由于核心数据建议,我必须创建反向关系,以便B的每个实例指向其父对象A,而C的每个实例指向其父对象B

A <->> B <->> C
要释放内存占用空间,并且由于目前不需要解析数据,我正在执行以下操作:

[a.managedObjectContext refreshObject:a mergeChanges:NO];
对于我刚刚解析的每个A

因为我需要解析大约10个A,它们都有大约10个B,它们都有大约10个C,所以生成了很多managedObject

问题-仪器 一切正常。唯一的问题是:当我打开分配工具时,我看到未发布的A、B和C。我没有从他们的帐户中得到任何有用的信息。 因为我的实际问题是一个更复杂的数据模型,所以活体对象成为了一个严重的内存问题。 有人能找出我做错了什么吗?使用正确的managedObject在其他managedObject上下文上调用refreshObjects也不起作用。只有一个硬的重置似乎起作用,但随后我松开了指向UI使用的活动对象的指针

我尝试过的其他解决方案
  • 我尝试创建单向关系,而不是双向关系。这会产生许多其他问题,导致核心数据不一致和奇怪的行为(例如悬空对象和核心数据生成1-n关系而不是n-n关系(因为反向关系未知)

  • 在检索任何对象上的
    NSManagedObjectContextDidSave
    通知时,我尝试刷新每个已更改或插入的对象

这两种“解决方案”(顺便说一句,它们都不起作用)似乎也有点老套。这不应该是一种方法。但是,应该有一种方法可以在不增加内存占用和保持UI平滑的情况下让这两种解决方案都起作用

-代码演示

-进一步调查
刷新mainContext中曾经使用过的每个对象(这是一项繁琐的工作)之后(在mainSave之后)对象的大小减少到48字节。这表明对象都有故障,但内存中仍有一个指针。当我们有大约40.000个对象都有故障时,内存中仍有1.920 MB,直到persistentManagedObjectContext重置后才会释放。这是我们不希望看到的因为我们失去了对任何managedObject的所有引用。

对于您为特定目的保留的每个
NSManagedObject上下文
,您将积累
NSManagedObject的实例

NSManagedObjectContext
只是一张便笺纸,如果您希望在
NSPersistentStore
中保留更改,然后放弃,则可以随意实例化并保存

对于解析操作(第3层),尝试为op创建MOC,进行解析,保存MOC,然后丢弃它

感觉你至少有一层MOC被强引用了太多


基本上问每个MOC的问题,“为什么我要让这个对象及其相关子对象保持活动状态”

我有一个导入帮助程序,它的功能非常类似

看看下面的代码,看看它是否对您有帮助

__block NSUInteger i = 0;
NSArray *jsonArray = ...
for (NSDictionary *dataStucture in jsonArray)
{
    [managedObjectContext performBlock:^{
        @autoreleasepool {
            i++;
            A *a = (A*)[self newManagedObjectOfType:@"A" inManagedObjectContext:managedObjectContext];
            [self parseData:[dataStucture objectForKey:@"a"]
                 intoObject:a
     inManagedObjectContext:managedObjectContext];

            [managedObjectContext refreshObject:a
                                   mergeChanges:YES];
            if (i > 20) // Arbitrary number here
            {
                NSError *error = nil;
                [managedObjectContext save:&error];
                [managedObjectContext reset];
            }

            [managedObjectContext refreshObject:a
                                   mergeChanges:YES];

        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [self saveMainThreadManagedObjectContext];

            NSLog(@"DONE");
            // parsing is done, now you see that there are still
            // A's, B's and C's left in memory.
            // Every managedObjectContext is saved and no references are kept
            // to any A, B and C so they should be released. This is not true,
            // so a managedObjectContext is keeping a strong reference to these
            // objects.
        });
    }];
}
罗宾

我有一个类似的问题,我的解决方式与您的不同。在您的情况下,您有第三个,IMO,冗余MOC,即父MOC。在我的情况下,我让两个MOC通过DidSave通知,以一种老式的方式,通过持久存储协调器进行通信。新的面向块的API使其更加简单和健壮。这ets让我重置子MOC。虽然您从第三个MOC获得了性能优势,但与我利用的SQLite行缓存相比,这并没有多大优势。您的路径消耗更多内存。最后,通过跟踪DidSave通知,我可以在创建项时修剪它们

顺便说一句,您的
MALLOC_-TINY
MALLOC_-SMALL
VM区域的大小也可能大幅增加。我的尾部修剪算法让分配器更快地重用空间,从而延缓了这些问题区域的增长。根据我的经验,这些区域是由于其较大的驻留空间mory footprint是我的应用程序Retweever被杀的主要原因。我怀疑你的应用程序也会遭遇同样的命运

当内存警告出现时,我调用以下代码段:

[self.backgroundMOC performBlock: ^{ [self.backgroundMOC reset]; }];

[self.moc save];

[self.moc.registeredObjects trimObjects];
-[NSArray(DDGArray)trimObjects]
只需遍历一个数组并刷新对象,从而对其进行修剪

总之,核心数据似乎为许多MOC中出现的项目实现了一种写时拷贝算法。因此,您以意外的方式保留了一些内容。我着重于在导入后断开这些连接,以最大限度地减少内存占用。由于SQLite行缓存,我的系统性能良好


Andrew

这是一个很好的长描述,但是如果没有代码,我怀疑你得到的不仅仅是猜测……在我看来,这不是一个代码非常相关的问题。它更概念化。但是我可以制作一个小的演示项目,如果有必要的话,你可以在其中遇到这个问题。我添加了一个XCode项目来说明pro错误在@autoreleasepool块中包装导入/解析处理,以便为每个对象A(及其B和C)清除池。@Rog不,可能不是这样。在autoreleasepool块之间包装它们并不能解决问题。以后
[self.backgroundMOC performBlock: ^{ [self.backgroundMOC reset]; }];

[self.moc save];

[self.moc.registeredObjects trimObjects];