3

我想缓存某个类的实例。该类保留其所有实例的字典,当有人请求新实例时,该类首先尝试满足来自缓存的请求。但是内存管理有一个小问题:字典缓存保留了插入的对象,因此它们永远不会被释放。我确实希望它们被解除分配,因此我不得不重载该release方法,并且当保留计数降至 1 时,我可以从缓存中删除实例并让它被解除分配。

这行得通,但我不喜欢乱搞该release方法并发现解决方案过于复杂。我想我可以使用一些不保留它存储的对象的散列类。有这样的吗?这个想法是,当某个实例的最后一个用户释放它时,该实例将自动从缓存中消失。

NSHashTable似乎是我正在寻找的东西,但文档谈到“在垃圾收集环境中支持弱关系”。没有垃圾收集它也能工作吗?


澄清:除非有人真的需要它们,否则我无法将实例保存在内存中,这就是为什么我想在最后一个“真实”用户释放实例时从缓存中清除实例。


更好的解决方案:这是在 iPhone 上,我想缓存一些纹理,另一方面,我想在最后一个真正的持有者释放它们时立即从内存中释放它们。更简单的编码方法是通过另一个类(我们称之为TextureManager)。此类管理纹理实例并缓存它们,以便从缓存中提供对具有相同名称的纹理的后续调用。当最后一个用户释放纹理时,无需立即清除缓存。我们可以简单地将纹理缓存在内存中,当设备内存不足时,我们会收到内存不足警告并可以清除缓存。这是一个更好的解决方案,因为缓存的东西不会污染Texture类,我们不必搞砸,release缓存命中的机会甚至更高。这TextureManager可以抽象成一个ResourceManager,这样它就可以缓存其他数据,而不仅仅是纹理。

4

6 回答 6

5

是的,您可以使用 NSHashTable 来构建本质上是非保留字典的内容。或者,您可以使用 NULL 调用 CFDictionaryCreate 来释放和保留回调。然后,由于免费桥接,您可以简单地将结果类型转换为 NSDictionary,并像使用普通 NSDictionary 一样使用它,除了不摆弄保留计数。

如果您这样做,字典不会自动将引用归零,您需要确保在解除分配实例时将其删除。

于 2009-01-06T09:36:27.640 回答
5

你想要的是一个归零弱引用(它不是“缓存管理算法的Graal”,它是一个众所周知的模式)。问题在于,Objective C 仅在垃圾收集运行时为您提供零弱引用,而不是在手动内存管理程序中。并且 iPhone 不提供垃圾收集(还)。

到目前为止,所有答案似乎都指向了半解决方案。

使用非保留引用是不够的,因为当引用的对象被释放时,您需要将其归零(或从字典中删除条目)。但是,这必须在调用该对象的 -dealloc 方法之前完成,否则缓存的存在会使您面临对象复活的风险。这样做的方法是在创建弱引用时动态地对对象进行子类化,并在动态创建的子类中覆盖 -release 以使用锁定和 -dealloc 将弱引用清零。

这通常有效,但对于免费桥接的 Core Foundation 对象来说,它会失败。不幸的是,唯一的解决方案是,如果您需要将该技术扩展到免费桥接对象,则需要一些黑客攻击和未记录的内容(请参阅此处获取代码和解释),因此不适用于 iOS 或您想在Mac 应用商店。

如果您需要在 Apple 商店销售,因此必须避免未记录的东西,您最好的选择是实现对保留缓存的锁定访问,然后在您想要释放内存时清除它以获取当前 -retainCount 值为 1 的引用。只要对缓存的所有访问都是在持有锁的情况下完成的,如果您在持有锁的同时观察到计数为 1,您就知道如果您从缓存中删除对象(并因此释放它),没有人可以复活该对象) 在放弃锁定之前。

对于 iOS,您可以使用 UIApplicationDidReceiveMemoryWarningNotification 来触发清理。在 Mac 上,您需要实现自己的逻辑:可能只是定期检查,甚至只是定期清理(两种解决方案也适用于 iOS)。

于 2011-01-13T16:49:24.180 回答
2

我刚刚通过使用 NSMutableDictionary 并注册 UIApplicationDidReceiveMemoryWarningNotification 来实现这种事情。在内存警告中,我从字典中删除了 retainCount 为 1 的任何内容...

于 2010-05-11T19:00:02.720 回答
1

使用 [NSValue valueWithNonretainedObject:] 将实例包装在 NSValue 中并将其放入字典中。在实例 dealloc 方法中,从字典中删除相应的条目。没有搞乱保留。

于 2010-05-11T20:05:38.737 回答
0

我的理解是你想实现缓存管理算法的Graal:丢弃不再使用的项目。

您可能需要考虑其他标准,例如删除最近最少请求的项目。

于 2009-01-06T09:57:37.643 回答
0

我认为我解决这个问题的方法是在某处维护一个单独的计数或标志,以指示缓存中的对象是否正在使用。然后,您可以在完成一个对象后检查它,或者每隔 n 秒运行一次检查以查看它是否需要释放。

我会避免任何涉及在从字典中删除对象之前释放对象的解决方案(使用 NSValuevalueWithNonretainedObject:将是实现此目的的另一种方法)。从长远来看,它只会给你带来问题。

于 2009-01-06T14:36:57.707 回答