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

我现在因为一个令人讨厌的核心数据问题而被困了大约两个星期.我阅读了很多博客文章,文章和SO问题/答案,但我仍然无法解决我的问题.

我运行了很多测试,并且能够将更大的问题减少到更小的问题.
这将是一个很大的解释,所以请跟我一起!

问题 – 数据模型

我必须得到以下datamodel:

对象A与对象B具有一对多的关系,它与对象C具有另一个一对多的关系.由于核心数据的建议,我必须创建反向关系,因此B的每个实例都指向其父A和相同的C指向其父B.

A <->> B <->> C

问题 – MOC设置

为了保持响应顺利,我创建了一个三级的managedobjectContext结构.

>父MOC – 使用nsprivateQueueConcurrencyType在自己的私有线程上运行,对persistentStoreCoordinator很紧张
> MainQueue MOC – 使用NSMainQueueConcurrencyType在mainThread上运行并具有父MOC 1
>对于每个解析操作,我创建第三个MOC,它也有其私有队列并具有父mainQueue MOC

我的主数据控制器作为观察者添加到MOC 2的NSManagedobjectContextDidSave通知中,因此每次MOC 2保存一个performBlock:触发MOC1,执行保存操作(由于performBlock :)异步执行.

问题 – 解析

为了将大型JSON文件解析为我的Core Data结构,我编写了一个循环解析器.该解析器首先创建一个新的MOC(3).然后它获取对象A的数据并解析其属性.然后解析器读出B的JSON关系并创建填充数据的相应对象.通过在A上调用addBObject:将这些新对象添加到A中.
因为解析器是循环的,解析B意味着解析C,这里也创建新对象并附加到B.
这一切都发生在执行块:在MOC 3上.

>解析(创建’A’对象并开始解析B)

>解析A(创建’B’对象,将它们附加到A并开始解析C)

>解析B(创建’C’对象,将它们附加到B)

>解析C(只将数据存储在C对象中)

在每次解析操作之后,我保存MOC 3并在mainThread上调度主MOC(2)的保存操作.由于NSManagedobjectContextDidSave通知,MOC 1将异步自动保存.

if (parsed){
            NSError *error = nil;
            if (![managedobjectContext save:&error])
                NSLog(@"Error while saving parsed data: %@",error);
        }else{
            // something went wrong,discard changes
            [managedobjectContext reset];
        }

        dispatch_async(dispatch_get_main_queue(),^{                
            // save mainQueueManagedobjectContext
            [[HWOverallDataController sharedOverallDataController] saveMainThreadManagedobjectContext];
        });

要释放我的内存占用,因为我现在不需要解析数据,我正在执行:

[a.managedobjectContext refreshObject:a mergeChanges:NO];

对于我刚解析的每一个A.

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

问题 – 仪器

一切正常.唯一的事情是:当我打开分配工具时,我看到未发布的A,B和C.我没有从他们的retainCounts或任何内容中获得任何有用的信息.
并且因为我的实际问题涉及更复杂的dataModel,所以生物对象成为严重的内存问题.
有人能弄清楚我做错了什么吗?使用正确的managedobject在其他managedobjectContexts上调用refreshObjects也不起作用.只有硬重置似乎有效,但随后我松开了指向UI使用的活动对象的指针.

其他解决方案我试过

>我尝试创建单向关系而不是双向关系.这会产生很多其他问题,导致核心数据不一致和奇怪的行为(例如悬空对象和核心数据生成1-n关系而不是n-n关系(因为反向关系未知).
>当我在任何对象上检索NSManagedobjectContextDidSave通知时,我尝试刷新每个已更改或插入的对象

这两种“解决方案”(顺便说一句,不起作用)似乎也有些笨拙.这不应该是要走的路.应该有一种方法可以在不增加内存占用和保持UI流畅的情况下实现这一点吗?

– CodeDemo

http://cl.ly/133p073h2I0j

– 进一步的调查

在mainContext(在mainSave之后)刷新每个使用过的对象(这是繁琐的工作)后,对象的大小减少到48个字节.这表示对象都是故障,但内存中仍有一个指针.当我们有大约40.000个对象都出现故障时,内存中仍有1.920 MB,在重置persistentManagedobjectContext之前永远不会释放.这是我们不想做的事情,因为我们放弃了对任何managedobject的每个引用.

解决方法

罗宾,

我有一个类似的问题,我解决的不同于你.在您的情况下,您有第三个IMO冗余MOC,即父MOC.就我而言,我让两个MOC以旧学校的方式通过DidSave通知通过持久性商店协调员进行通信.新的面向块的API使这更加简单和健壮.这让我可以重置子MOC.虽然您从第三个MOC中获得了性能优势,但与我利用的sqlite行缓存相比,它并没有那么大的优势.你的路径消耗更多的内存.最后,我可以通过跟踪DidSave通知来修剪项目.

顺便说一句,您可能也正在遭受MALLOC_TINY和MALLOC_SMALL虚拟机区域大小的大幅增加.我的尾随修剪算法让分配器可以更快地重用空间,从而延缓这些有问题区域的增长.根据我的经验,这些地区由于其庞大的居民记忆足迹而成为我的应用程序Retweever被杀害的主要原因.我怀疑你的应用程序遭受同样的命运.

当内存警告到来时,我打电话给下面的片段:

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

[self.moc save];

[self.moc.registeredobjects trimObjects];

– [NSArray(DDGArray)trimObjects]只是通过一个数组并刷新对象,从而修剪它们.

总之,Core Data似乎为许多MOC中出现的项目实现了写入算法的副本.因此,你会以意想不到的方式保留东西.我专注于在导入后断开这些连接以最小化我的内存占用.由于sqlite行缓存,我的系统似乎可以很好地执行.

安德鲁

相关文章

UITabBarController 是 iOS 中用于管理和显示选项卡界面的一...
UITableView的重用机制避免了频繁创建和销毁单元格的开销,使...
Objective-C中,类的实例变量(instance variables)和属性(...
从内存管理的角度来看,block可以作为方法的传入参数是因为b...
WKWebView 是 iOS 开发中用于显示网页内容的组件,它是在 iO...
OC中常用的多线程编程技术: 1. NSThread NSThread是Objecti...