Core Data内存使用和内存警告

9

我有一个问题。 我在Core Data中拥有一组图像数据库。 我获取了所有的图像(大约80MB)并将其放在NSMutableArray中。 对象已正确被取出:

NSArray *fetchResults = [self.managedObjectContext executeFetchRequest:request error:&error];
self.cache = [NSMutableArray arrayWithArray:fetchResults];
for (ImageCache *imageObject in self.cache) {
    NSLog(@"Is fault? %i", [imageObject isFault]);
}

阅读日志,我发现对象都已经正确地故障了。然而,使用Instruments工具,我看到80MB的内存被使用了。我认为这就是Core Data缓存结果的原因,并且当需要释放内存时应该释放它。但是(这是我的“问题”),如果我模拟内存警告,什么也不会发生!这80MB仍然存在。
查看Instruments - 分配情况,80MB由许多Malloc使用:(示例)
图表 类别 实时字节数 # 存活 # 瞬态 总字节数 # 总数 # 分配(净/总) 0 Malloc 176,00 KB 8,59 MB 50 57 18,39 MB 107 %0.00, %0.00 0 Malloc 200,00 KB 8,20 MB 42 460 98,05 MB 502 %0.00, %0.04 0 Malloc 168,00 KB 7,05 MB 43 19 10,17 MB 62 %0.00, %0.00
这是整个调用树的图片链接:https://www.dropbox.com/s/du1b5a5wooif4w7/Call%20Tree.png 有什么想法吗?谢谢

也许Core Data会在“内存警告级别2”上释放内存?您的情况是否可能导致低内存崩溃? - brigadir
有没有什么“魔法方法”可以模拟内存警告级别2?或者说,“简单地”我必须消耗内存吗? - LombaX
嗯...但是如果我把应用程序放在后台,它会被冻结,所以我不会收到系统的消息,对吧?然而,我尝试了其他方法。我创建了一个开始浪费内存的循环(一个简单的随意NSData数组)。我尝试了各种数量来增加总内存(有时让iOS杀死我的应用程序),但Core Data从未释放缓存 :-/(显然是在设备上尝试,而不是在模拟器上)。 - LombaX
1
嗯...在这个链接中,文档说内存警告不会发送给已挂起的应用程序:http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/PerformanceTuning/PerformanceTuning.html “当系统向您的应用程序分派低内存警告时,请立即响应。每当可用内存量下降到安全阈值以下时,iOS会通知所有正在运行的应用程序。(它不会通知已挂起的应用程序。” - LombaX
只有在启动后台任务时才会收到didreceivememorywarning,哈哈。 - LombaX
显示剩余3条评论
2个回答

9
好的,我明白了为什么会发生这种情况。当您请求获取一个实体时,即使启用了故障,该实体的所有数据都会加载到内存中,包括大型二进制数据。您可以使用多种方法解决此问题:
1- 在NSFetchRequest上设置以下内容:[request setIncludesPropertyValues:NO]; 设置为NO,数据不会立即加载到缓存中,只有在请求时(访问属性并触发故障时)才会加载。但是这里有个“问题”。即使您再次尝试故障该属性(因为您不需要立即使用它并想要释放内存,使用[self.managedObjectContext refreshObject:object mergeChanges:NO];),内存也不会被释放。缓存保持活动状态,直到重置managedObjectContext。
这个更好:
2- 您可以将数据拆分为单独的实体。在我的情况下,我只有两个属性:一个URL和图像数据。我将数据拆分为两个实体,具有1:1的关系:imagecache和imagedata。对“imagecache”实体的所有行进行fetchRequest(具有url属性),并且像之前的解决方案一样,没有缓存内存。属性imagecache.relationship.image被正确地故障。访问此属性会导致故障触发并填充缓存。但是,在这种情况下,在“imagecache”对象(“父”对象)上执行[self.managedObjectContext refreshObject:object mergeChanges:NO];会立即释放缓存和内存,并重新故障imagecache.relationship.image属性。注意:不要在“子”对象上执行此操作,如果您执行[self.managedObjectContext refreshObject:object.relationship mergeChanges:NO],由于某种原因,缓存不会被释放。我认为这就是您遍历关系的原因。
3- 我说过这主要是一个学术问题,对于这些问题,真正的“全天候”解决方案(性能更好,更少的头疼)是避免将大数据保存在core data数据库中。您可以将数据保存为文件并仅存储引用(文件路径),或者使用iOS 5,您可以在core data模型中的任何“Data”属性上设置“use external storage”。这将为您完成所有工作。

1
嘿,我只是想知道你是否找到了其他解决这个问题的方法。不幸的是,我没有任何可以使用第三种解决方案的大型NSData内存对象。相反,我有数十万个作为地图注释显示的对象。我分批获取数据,以确保如果设备没有足够的内存,我可以限制显示的数据量。但是当我收到内存警告并调用refreshObject:mergeChanges:时,内存不像你提到的那样受影响,最终导致内存崩溃。有什么想法吗? - horsejockey

0

我认为你应该批量将较少的对象加载到内存中。

CoreData释放的内存是在后台进行的,您不必为此编程;坏消息是它发生在幕后,因此可能会“神奇地”消耗内存。

解决方法有很多;例如,使用谓词仅选择您绝对需要的行;不要调用一般性的获取所有内容,然后逐个浏览列表。很可能当您进行一般性调用并且CoreData尝试加载所有对象时,您会崩溃。


是的,我采用的第一个解决方案是仅获取所需的数据。这主要是一个“学术”问题。文档对此很清楚,缓存管理由Core Data完成,所有操作都在幕后进行,但它甚至说在低内存情况下,内存会被释放。我期望在内存警告后看到Core Data使用的内存减少。没有看到这一点,我认为我的代码有问题...!我想亲眼看到Core Data在低内存情况下释放内存 :-) - LombaX
只是一个更新:我尝试占用内存(一个简单的循环分配一些NSData实例),但Core Data从未释放内存(我尝试了很多次,分配100MB的NSData,然后是200,然后是300...直到应用程序崩溃)。它似乎缓存了fetchrequest的所有数据而不释放它,永远不会!我知道我可以使用其他方法来达到我的目的,但这似乎很奇怪。如果它们继续使用RAM,那么faulting属性的含义是什么? - LombaX

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