自動解放プール内に自動解放されたオブジェクト ( 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
NSCache
このイメージを同じ SKU/製造元に対して繰り返し呼び出している場合、ロードするイメージを保存するためにを使用することで、メモリを大幅に節約できます (パフォーマンスも向上します) 。同じ画像を複数回リクエストした場合に、重複する画像が作成されるのを防ぎます。を使用NSCache
すると、その問題が解決します。これNSCache
は便利なキーです (メーカー コードと SKU から構成された文字列を使用することもできますが、それはあなた次第です)。
私はdispatch_once
2つの静的変数を設定するのに役立ちました:
率直に言うと、シングルトン インスタンスのインスタンス変数としてdocumentsPath
および/またはを移動し、これらの変数をを使用するのではなく、適切な方法で設定する傾向がありますが、あなたが私たちと共有した方法。cache
init
dispatch_once
本当にマイナーな変更ですが、私は常に変数名にキャメルケース (小文字で始まる) を使用しているためItemSKU
、itemSKU
.
私はあなたのブロックを採用しましたが、たとえば、@autoreleasepool
このメソッドを単一のループ内から何度も呼び出す場合を除き、通常は必要ありません。for
これらがテーブル ビューまたはコレクション ビューで使用されるサムネイルである場合、@autoreleasepool
ブロックは必要ありません。しかし、これらの非常に特殊なシナリオのいずれかが当てはまる場合に備えて、そこに保管しておきました。
個人的には、@autoreleasepool
何らかの値を返すコードではなく、自己完結型のコード ブロックを囲むブロックを使用します。しかし、状況がそれを必要とする場合は、上記のようなことを行うことができます.
を使用するとcache
、同じイメージに対してこのメソッドを複数回呼び出す場合、(メモリ消費とパフォーマンスの両方の点で) 大きな影響があります。static
とdispatch_once
forの使用によるdocumentsPath
パフォーマンスへの影響はわずかですが、これを頻繁に呼び出している場合は、その影響が顕著になり、検討する必要があるかもしれません。
ブロックの使用は@autoreleasepool
、メモリの増加が見られる場合に役立ちますが、後でそれが完了すると妥当なレベルに戻りますが、単にその「最高水準点」を減らしたいだけです。メモリがまったく低下しないことが問題である場合、自動解放プールは役に立ちません。問題は別のところにあります。
これを自分で試して、プロファイラーで実行し、パフォーマンスとメモリ使用量を確認してください。個人的には、一般的にキャッシュの使用に焦点を当てており、このメソッドの呼び出し方に特異な点がない限り (たとえば、1 回のループ@autoreleasepool
で何千回も呼び出している場合など) 、それほど心配する必要はありませんが、for
考慮すべきこと。@autorelease
ほとんどのシナリオでは、ブロックではなくキャッシュを使用することで真のメリットが得られます。