你正在创建一个自动释放池内部的自动释放的对象(imageWithData
),返回这一点,但随后立即排水池中。最简单的解决方法是删除该自动释放池。为什么有这个池呢?只需立即排空NSData
?但你并不需要一个NSData
在所有的,因为你可以只直接检索图像:
@implementation Helpers
+ (UIImage *) getThumbnailImageIfExists:(NSString *)ItemSKU withManufacturer: (NSNumber *) aManufacturerID {
NSString *fileName = [[[SharedFunctions sharedInstance] getLargeFileName:[aManufacturerID stringValue] withPhotoName:ItemSKU] stringByReplacingOccurrencesOfString:@"_lg.jpg" withString:@"_tn.jpg"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *savePath = [documentsPath stringByAppendingPathComponent:[fileName lowercaseString]];
return [UIImage imageWithContentsOfFile:savePath];
}
@end
如果你真的想确保各种字符串和数组变量(即fileName
,paths
,documentsPath
和savePath
)不会被放到调用者的自动释放池中,你可以解决这个问题,但我不知道这是多么的重要(至少与池中的NSData
相比)。
考虑这个替代实现:
+ (UIImage *)getThumbnailImageIfExists:(NSString *)itemSKU withManufacturer:(NSNumber *)aManufacturerID
{
UIImage *image;
static NSString *documentsPath;
static NSCache *cache;
// create docsPath and cache once and only once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentsPath = searchPaths[0];
cache = [[NSCache alloc] init];
cache.countLimit = 100;
});
// now do your image retrieval
@autoreleasepool {
NSString *fileName = [[[SharedFunctions sharedInstance] getLargeFileName:[aManufacturerID stringValue] withPhotoName:itemSKU] stringByReplacingOccurrencesOfString:@"_lg.jpg" withString:@"_tn.jpg"];
NSString *savePath = [documentsPath stringByAppendingPathComponent:[fileName lowercaseString]];
image = [cache objectForKey:savePath];
if (!image)
{
image = [[UIImage alloc] initWithContentsOfFile:savePath]; // note, not an autoreleased object
[cache setObject:image forKey:savePath];
}
}
return image;
}
我做了几件事情在这里:
像以前一样,我已经去除了不必要的NSData
逻辑。无需将文件加载到NSData
中,然后从中创建UIImage
,然后仅丢弃NSData
。
如果您对同一个SKU /制造商重复调用此图像,则使用NSCache
来存储加载的图像会节省大量的内存(以及性能改进)。如果您碰巧多次请求同一图像,它会阻止您创建重复图像。使用NSCache
解决了这个问题。通过我的图片文件名键入NSCache
,这是一个方便的使用键(尽管您也可以使用一些由制造商代码和SKU组成的字符串;这取决于您)。
我援用的dispatch_once
自己设置两个静态变量:
坦率地说,我倾向于在适当的init
方法移动documentsPath
和/或cache
一些单一实例的实例变量并设置这些变量,而比使用dispatch_once
,但我试图告诉你如何通过修改你与我们分享的方法来做到这一点。
真的很小的变化,但我总是用变量名称来使用camelCase(以小写字母开头),所以我将ItemSKU
更改为itemSKU
。例如,当我使用@autoreleasepool
块时,通常不需要这样做,除非您在单个for
循环内调用此方法的次数很多。如果这些是在表格视图或集合视图中使用的缩略图,则不需要@autoreleasepool
块。但是我保留在那里以防万一这些特殊情况适用。
就个人而言,我使用围绕自包含代码块的@autoreleasepool
块,而不是返回某个值的代码。但是如果你遇到这种情况,你可以像上面那样做。
使用该cache
都会有很大的影响(无论是在内存消耗和性能方面)如果你调用此方法一次以上相同的图像。使用static
和dispatch_once
代替documentsPath
的性能影响不大,但如果您打电话给很多,那么它会变得很明显,并且您可能会考虑优化。
使用@autoreleasepool
块可以在内存增加的情况下使用,但在完成后会回落到合理的水平,但您只是想减少“高水位”。如果问题是内存永远不会下降,那么自动释放池不会帮助你;问题在于其他地方。
你应该自己动手,通过分析器运行它,并检查性能和内存使用情况。就个人而言,我通常会关注缓存的使用,不要太担心@autoreleasepool
,除非有什么特别的关于你如何调用这种方法(例如,你在一个for
循环中称它为成千上万次),但是这是需要考虑的事情。对于大多数情况下,真正的好处将来自使用缓存,而不是@autorelease
块。
来源
2013-05-17 19:06:18
Rob
你确定问题在这里,而不是使用这些图像的任何代码? – rmaddy
不,我可能在将这个项目转换为ARC时做了一些假设 - 正如你所看到的我的堆积增长是荒谬的 – Slee
@rmaddy它看起来你是对的,这是一个开始支持iOS 4的旧项目它有许多代表没有设置为弱点,所以整个观点被抛弃到记忆中 - 作为答案添加,所以我可以给你信用。 – Slee