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

标签 ios core-data memory

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

我进行了大量测试,并且能够将较大的问题简化为较小的问题。
这将是一个很大的解释,所以请跟我来!

问题 - 数据模型

我必须关注 数据模型 :

对象 A 与对象 B 有一对多的关系,而对象 B 与对象 C 有另一个一对多的关系。由于核心数据建议,我必须创建逆关系,因此 B 的每个实例都指向其父级 A,对于C 指向其父级 B。

A <->> B <->> C

问题 - MOC 设置

为了保持响应顺畅,我创建了一个三级 managedObjectContext 结构。
  • 父 MOC - 使用 NSPrivateQueueConcurrencyType 在自己的私有(private)线程上运行,紧到persistentStoreCoordinator
  • MainQueue MOC - 使用 NSMainQueueConcurrencyType 在主线程上运行并且有父 MOC 1
  • 对于每个解析操作,我创建了第三个 MOC,它也有它的私有(private)队列和父 mainQueue MOC

  • 我的主数据 Controller 作为观察者添加到 NSManagedObjectContextDidSave MOC 2 的通知,所以每次 MOC 2 都会保存一个 performBlock:在 MOC1 上触发执行保存操作(由于 performBlock: 异步)。

    问题 - 解析

    为了将大型 JSON 文件解析为我的核心数据结构,我编写了一个循环解析器。这个解析器首先创建一个新的 MOC (3)。然后它获取对象 A 的数据并解析其属性。然后解析器读出 B 的 JSON 关系并创建相应的填充数据的对象。这些新对象通过调用 addBObject: 添加到 A。在 A 上。
    因为解析器是循环的,所以解析 B 意味着解析 C,这里也创建了新对象并将其附加到 B。
    这一切都发生在 performBlock:在 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 或其他任何地方得到任何有用的信息。
    并且因为我的实际问题涉及更复杂的数据模型,活对象成为一个严重的内存问题。
    有人能弄清楚我做错了什么吗?使用正确的 managedObject 在其他 managedObjectContexts 上调用 refreshObjects 也不起作用。只有一个硬reset似乎工作但后来我失去了指向 UI 使用的活对象的指针。

    我试过的其他解决方案
  • 我尝试创建单向关系而不是双向关系。这会产生许多其他问题,导致 Core Data 不一致和奇怪的行为(例如悬空对象和 Core Data 生成 1-n 关系而不是 n-n 关系(因为逆关系未知)。
  • 当我检索 NSManagedObjectContextDidSave 时,我尝试刷新每个更改或插入的对象。关于任何对象的通知

  • 这两个“解决方案”(顺便说一下不起作用)似乎也有点老套。这不应该是要走的路。但是,应该有一种方法可以在不增加内存占用和保持 UI 平滑的情况下使其工作?

    - 代码演示

    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 行缓存,我的系统似乎表现良好。

    安德鲁

    关于ios - 导入大型数据集时的核心数据内存使用情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13129035/

    相关文章:

    ios - UICollectionView:重新加载数据,而不删除单元内动画

    ios - 降低 UIImage 的 DPI 分辨率

    xml - 在后台线程上将 XML 解析为 CoreData 以不锁定 UI

    android - 为什么这些内存分配数字不相加?

    Python无限期挂起试图删除深度递归对象

    ios - 以编程方式更改类型

    ios - 无法将 NSAttributedString.DocumentAttributeKey 类型的值转换为 .DocumentReadingOptionKey

    core-data - 无法在 Xcode 中打开核心数据模型

    ios - 使用 Core Data 持久保存 TableView Cell 图像

    c++ - GlobalMemoryStatusEx() 给出的总虚拟内存为 127 TeraByte