1. ホーム
  2. iphone

[解決済み] UIImage ImageNamedを払拭する。FUD

2022-09-24 05:23:58

質問

2014年2月編集 この質問はiOS 2.0からのものであることに注意してください! 画像の要件と取り扱いは、それ以来大きく前進しています。Retinaは画像をより大きくし、読み込みも若干複雑になっています。iPadとRetina画像のためのビルトインサポートで。 あなたのコードでImageNamedを使用する必要があります。 .

多くの人が imageNamed が悪いと言う人が多いのですが、それと同じ数の人がパフォーマンスが良いと言っています。 UITableView s. 参照 このSOの質問 を参照してください。 この記事 iPhoneDeveloperTips.comに掲載されています。

UIImage 's imageNamed メソッドは以前はリークしていたので避けるのが最善でしたが、最近のリリースで修正されました。私は、画像のキャッシュをシステムに任せていいところと、自分でやる必要があるところを合理的に判断するために、キャッシュ アルゴリズムをもっと理解したいと思います。私の現在の基本的な理解では、単純な NSMutableDictionaryUIImages はファイル名で参照される。大きくなり、メモリがなくなるとかなり小さくなります。

例えば、誰か確実に知っている人はいますか、画像キャッシュの後ろにある imageNamed に反応しないことを 知っている人はいますか? didReceiveMemoryWarning ? Appleがこれをしないとは考えにくい。

キャッシュ アルゴリズムについて何かおわかりになることがあれば、ここに投稿してください。

どのように解決するのですか?

tldr: ImagedNamedは大丈夫です。メモリをうまく処理します。それを使って心配するのはやめましょう。

2012年11月編集 : この質問はiOS 2.0のものであることに注意してください。画像の要件と処理は、それ以来大きく変化しています。Retinaは画像をより大きくし、それらをロードすることは若干複雑になっています。iPadとRetina画像のためのビルトインサポートで、あなたは確かにあなたのコードでImageNamedを使用する必要があります。さて、後学のために。

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

<ブロッククオート

iPhone OS 2.x では、メモリ警告の後でも imageNamed: のキャッシュがクリアされないという問題がありました。同時に +imageNamed: はキャッシュのためではなく、利便性のために多く使われるようになり、そのことが問題を必要以上に拡大させてしまったのでしょう。

という警告が出る一方で

<ブロッククオート

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

キャッシュの一般的な動作に関しては、ファイル名に基づいてキャッシュされ (したがって、同じ名前を持つ 2 つの +imageNamed: インスタンスは、同じキャッシュ データへの参照となります)、+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);

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