你正在創建一個自動釋放池內部的自動釋放的對象(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