画像 IO
考慮する価値のある遅延 — Kevin Passat
第 13 章「効率的な描画」では、コア グラフィックスの描画に関連するパフォーマンスの問題とその修正方法について説明しました。描画性能と密接に関係しているのが画像性能です。この章では、フラッシュ ドライブまたはネットワークからの画像の読み込みと表示を最適化する方法を見ていきます。
描画に費やされる実際の時間は、通常、パフォーマンスの要因ではありません。画像は大量のメモリを消費し、表示する必要があるすべての画像をメモリに保持することは不可能であるため、アプリケーションの実行中に画像を定期的にロードおよびアンロードする必要があります。
画像ファイルの読み込み速度は、CPU と IO (入出力) の両方に影響されます。 iOS デバイスのフラッシュ メモリはすでに従来のハード ドライブよりもはるかに高速ですが、それでも RAM よりも約 200 倍遅いため、遅延を避けるために読み込みを慎重に管理する必要があります。
可能な限り、起動中や画面切り替え中など、プログラムのライフサイクルの目立たない時間帯に画像を読み込むようにしてください。ボタンを押してからボタン応答イベントまでの最大遅延は約 200 ミリ秒で、これはアニメーションの各フレーム切り替えの 16 ミリ秒よりもはるかに小さくなります。アプリの最初の起動時に画像をロードできますが、アプリが 20 秒以内に起動できない場合は、iOS 検出タイマーによってアプリが終了されます (起動に 2 ~ 3 秒以上かかると、ユーザーから苦情が来ます)。
すべてを前もって読み込むのが賢明でない場合もあります。たとえば、数千枚の写真を含む写真カルーセルを考えます。ユーザーは写真をスムーズかつ迅速に反転できることを望んでいます。そのため、すべての写真を事前にロードすることは不可能であり、時間とメモリを大量に消費します。
場合によっては、リモート ネットワーク接続から画像をダウンロードする必要がある場合もあります。これには、ディスクからロードするよりも時間がかかり、接続の問題により (数秒の試行後) ロードに失敗する場合もあります。メインスレッドでネットワークをロードして待機を引き起こすことはできないため、バックグラウンド スレッドが必要です。
第 12 章「パフォーマンスチューニング」の連絡先リストの例では、画像が非常に小さいため、メインスレッドで同期して読み込むことができます。ただし、大きな画像の場合、読み込みに時間がかかり、スライドが滑らかでなくなるため、これは適切ではありません。スライド アニメーションはメイン スレッドの実行ループで更新されるため、レンダリング サービス プロセスで実行される CPU 関連のパフォーマンスの問題がさらに多くなります。
リスト 14.1 は、UICollectionView を通じて実装された基本的な画像トランスミッターを示しています。画像は、メインスレッドの -collectionView:cellForItemAtIndexPath: メソッドで同期的にロードされます (図 14.1 を参照)。
リスト 14.1 UICollectionView を使用して実装された画像トランスミッター
<br />
#import "ViewController.h"@interface ViewController() <UICollectionViewDataSource>@property (nonatomic, copy) NSArray *imagePaths;@property (nonatomic, weak) IBOutlet UICollectionView *collectionView;@end@implementation ViewController- (void)viewDidLoad{ //set up data self.imagePaths = [[NSBundle mainBundle] pathsForResourcesOfType:@"png" inDirectory:@"Vacation Photos"]; //register cell class [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];}- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return [self.imagePaths count];}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ //dequeue cell UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; //add image view const NSInteger imageTag = 99; UIImageView *imageView = (UIImageView *)[cell viewWithTag:imageTag]; if (!imageView) { imageView = [[UIImageView alloc] initWithFrame: cell.contentView.bounds]; imageView.tag = imageTag; [cell.contentView addSubview:imageView]; } //set image NSString *imagePath = self.imagePaths[indexPath.row]; imageView.image = [UIImage imageWithContentsOfFile:imagePath]; return cell;}@end
<br />
<br />
図 14.1 動作中の画像トランスミッター
トランスミッターの画像サイズは 800x600 ピクセルの PNG (iPhone の場合) 5、1/60秒かかります約700KBの画像を読み込みます。テレポーターがスクロールすると、画像がリアルタイムで読み込まれるため、(予想どおり) フリーズが発生しました。時間分析ツール (図 14.2) は、UIImage の +imageWithContentsOfFile: メソッドに多くの時間が費やされていることを示しています。画像の読み込みがボトルネックの原因であることは明らかです。