117

2014 年 2 月の編集:この質問は iOS 2.0 のものであることに注意してください。それ以来、画像の要件と処理は大きく変わりました。Retina では画像が大きくなり、読み込みが少し複雑になります。iPad および Retina 画像のサポートが組み込まれているため、必ずコード内で ImageNamed を使用する必要があります

多くの人imageNamedが悪いと言っていますが、同じ数の人がパフォーマンスが良いと言っています - 特にUITableViews をレンダリングするとき。たとえば、この SO の質問を参照するか、iPhoneDeveloperTips.comのこの記事を参照してください。

UIImageimageNamedメソッドはリークしていたため、回避するのが最善でしたが、最近のリリースで修正されました。システムが画像をキャッシュすることを信頼できる場所と、さらに一歩進んで自分で行う必要がある場所について、合理的な決定を下すために、キャッシュアルゴリズムをよりよく理解したいと思います。私の現在の基本的な理解は、ファイル名によって参照されるNSMutableDictionary単純なものだということです。UIImagesそれは大きくなり、メモリがなくなるとずっと小さくなります。

たとえば、背後の画像キャッシュimageNamedが応答しないことを誰かが確実に知っていdidReceiveMemoryWarningますか? Apple がこれを行わない可能性は低いようです。

キャッシュ アルゴリズムに関する洞察があれば、ここに投稿してください。

4

2 に答える 2

85

tldr: ImagedNamed は問題ありません。メモリをうまく処理します。それを使用して、心配するのをやめてください。

2012 年 11 月の編集: この質問は iOS 2.0 のものであることに注意してください。それ以来、画像の要件と処理は大きく変わりました。Retina では画像が大きくなり、読み込みが少し複雑になります。iPad および Retina 画像のサポートが組み込まれているため、必ずコード内で ImageNamed を使用する必要があります。さて、後世のために:

Apple Dev Forumsの姉妹スレッドは、より良いトラフィックを受け取りました。具体的には、 Rincewindがいくつかの権限を追加しました。

iPhone OS 2.x には、メモリ警告の後でも imageNamed: キャッシュがクリアされないという問題があります。同時に、+imageNamed: はキャッシュのためではなく利便性のために多くの用途を獲得しており、おそらく問題を本来よりも拡大しています。

と警告しながら

速度の面では、何が起こっているのかについて一般的な誤解があります。+imageNamed: が行う最大のことは、ソース ファイルから画像データをデコードすることです。これにより、ほとんどの場合、データ サイズが大幅に増大します (たとえば、画面サイズの PNG ファイルは、圧縮すると数十 KB を消費する可能性がありますが、0.5 MB 以上を消費します)。解凍 - 幅 * 高さ * 4)。対照的に、 +imageWithContentsOfFile: は、画像データが必要になるたびにその画像を解凍します。ご想像のとおり、画像データが 1 回だけ必要な場合は、キャッシュされたバージョンの画像が必要以上に長く使用されることを除いて、ここでは何も得られません。ただし、頻繁に再描画する必要がある大きな画像がある場合は、代替手段がありますが、主にその大きな画像の再描画を避けることをお勧めします:)。

キャッシュの一般的な動作に関しては、ファイル名に基づいてキャッシュします (したがって、同じ名前の +imageNamed: の 2 つのインスタンスは、同じキャッシュ データへの参照になるはずです)。 +imageNamed:. iPhone OS 2.x では、バグにより、メモリ警告が受信されたときにキャッシュが縮小されません。

私の理解では、+imageNamed: キャッシュは iPhone OS 3.0 のメモリ警告を尊重する必要があります。機会があればテストして、そうでない場合はバグを報告してください。

それで、あなたはそれを持っています。imageNamed: 窓を壊したり、子供を殺したりしません。とてもシンプルですが、最適化ツールです。悲しいことに、それはひどく名前が付けられており、これほど使いやすい同等のものはありません。

それを修正するためにUIImageにカテゴリを追加しました:

// header omitted
// Before you waste time editing this, please remember that a semi colon at the end of a method definition is valid and a matter of style.
+ (UIImage*)imageFromMainBundleFile:(NSString*)aFileName; {
    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
    return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", bundlePath,aFileName]];
}

Rincewind には、独自の最適化されたバージョンを構築するためのサンプル コードも含まれています。メンテナンスする価値があるとは思えませんが、完全を期すためにここに示します。

CGImageRef originalImage = uiImage.CGImage;
CFDataRef imageData = CGDataProviderCopyData(
     CGImageGetDataProvider(originalImage));
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData(imageData);
CFRelease(imageData);
CGImageRef image = CGImageCreate(
     CGImageGetWidth(originalImage),
     CGImageGetHeight(originalImage),
     CGImageGetBitsPerComponent(originalImage),
     CGImageGetBitsPerPixel(originalImage),
     CGImageGetBytesPerRow(originalImage),
     CGImageGetColorSpace(originalImage),
     CGImageGetBitmapInfo(originalImage),
     imageDataProvider,
     CGImageGetDecode(originalImage),
     CGImageGetShouldInterpolate(originalImage),
     CGImageGetRenderingIntent(originalImage));
CGDataProviderRelease(imageDataProvider);
UIImage *decompressedImage = [UIImage imageWithCGImage:image];
CGImageRelease(image);

このコードのトレードオフは、デコードされたイメージがより多くのメモリを使用することですが、レンダリングはより高速になります。

于 2009-06-02T07:33:52.963 に答える
5

私の経験では、imageNamedによって作成された画像キャッシュはメモリ警告に応答しません。mem管理の範囲で可能な限り無駄のない2つのアプリケーションがありましたが、memが不足しているために不可解にクラッシュしていました。imageNamedを使用して画像を読み込むのをやめると、両方のアプリケーションが劇的に安定しました。

どちらのアプリケーションもやや大きな画像をロードしたことは認めますが、まったく異常なことは何もありません。最初のアプリケーションでは、ユーザーが同じ画像に2回戻る可能性が低いため、キャッシュを完全にスキップしました。2番目の例では、UIImagesをNSMutableDictionaryに保持し、メモリ警告を受け取った場合にその内容をフラッシュするという、あなたが述べたことを実行する非常に単純なキャッシュクラスを構築しました。imageNamed:がそのようにキャッシュされた場合、パフォーマンスの向上は見られなかったはずです。これはすべて2.2で実行されていました-これに3.0の影響があるかどうかはわかりません。

この問題に関する他の質問は、私の最初のアプリからここにあります: UIImageキャッシングに関するStackOverflowの質問

もう1つの注意-InterfaceBuilderは内部でimageNamedを使用します。この問題が発生した場合に注意すべき点があります。

于 2009-06-02T02:22:03.463 に答える