10

私は、iOS のネイティブの写真アプリに非常によく似た UITableView に取り組んでいます。各行に 4 つの画像サムネイルを含む多くの行があります。(つまり、各 UITableViewCell には 4 つの UIImageView があります) Core Data からロードされたすべてのサムネイル。

実装を何度も修正しましたが、パフォーマンスの改善は見られますが、写真アプリほどスムーズにスクロールすることはできません。

最高のパフォーマンスを得るために写真を適切にキャッシュする方法についてアドバイスが必要です。これは私が試したものです:

1. 初めての試み(スクロール時のラグが激しい)

  • 画像は CoreData に Transformable 型で保存されます。
  • cellForRow 関数では、各画像がオンザフライで CoreData から取得されます。

2. 2 回目の試行 (高速ですが、スクロール時に少し遅れます)

  • 画像は、CoreData で「外部ストレージ」オプションが選択されたタイプのバイナリ データで保存されます。
  • cellForRow 関数では、各画像は最初に Core Data から読み込まれ、次にメモリ内の NSCache に格納されるため、次回 cellForRow が起動したときに、可能であれば NSCache から UIImage を直接使用します。

NSCache を使用して CoreData からロードされた画像をキャッシュした後、スクロールは目に見えて速くなりますが、NSCache でまだ画像を使用できない場合でも CoreData から画像をロードする必要があるため、スクロールは時々ぎくしゃくします。

したがって、より良い方法があるはずです。すべての画像をメモリにプリロードできますが、画像の数または行が多い可能性があるため、画像をプリロードする予定はまったくありませんでした。

cellForRowAtIndexPath で画像をより速くロードするには、他に何ができますか?

4

3 に答える 3

26

データの出所に関係なくスクロールをスムーズに保つには、別のスレッドでデータをフェッチし、データがメモリにある場合にのみUIを更新する必要があります。グランドセントラルディスパッチは行く方法です。self.photosこれは、画像ファイルへのテキスト参照を含む辞書があることを前提としたスケルトンです。画像のサムネイルは、ライブ辞書に読み込まれる場合と読み込まれない場合があります。ファイルシステムキャッシュにある場合とない場合があります。それ以外の場合は、オンラインストアから取得されます。Core Dataを使用することもできますが、スムーズなスクロールの鍵は、データがどこから来たとしても、データを待たないことです。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    static NSString *CellIdentifier = @"Photo Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    //identify the data you are after
    id photo = [self.photos objectAtIndex:indexPath.row];
        // Configure the cell based on photo id

      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //move to an asynchronous thread to fetch your image data
            UIImage* thumbnail = //get thumbnail from photo id dictionary (fastest)
            if (!thumbnail) {    //if it's not in the dictionary
                thumbnail =      //get it from the cache  (slower)
                                 // update the dictionary
                if (!thumbnail) {   //if it's not in the cache
                  thumbnail =       //fetch it from the (online?) database (slowest)
                                    // update cache and dictionary
                    }
                }
            }
            if (thumbnail) {  
                dispatch_async(dispatch_get_main_queue(), ^{
                //return to the main thread to update the UI
                    if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
                    //check that the relevant data is still required
                        UITableViewCell * correctCell = [self.tableView cellForRowAtIndexPath:indexPath];
                        //get the correct cell (it might have changed)
                        [[correctCell imageView] setImage:thumbnail];
                        [correctCell setNeedsLayout];
                    }
                });
            }
        });
    return cell;
    }

ある種のシングルトンイメージストアマネージャーを使用している場合は、マネージャーがキャッシュ/データベースアクセスの詳細を処理することを期待します。これにより、この例が簡略化されます。

この部分

            UIImage* thumbnail = //get thumbnail from photo id dictionary (fastest)
            if (!thumbnail) {    //if it's not in the dictionary
                thumbnail =      //get it from the cache  (slower)
                                 // update the dictionary
                if (!thumbnail) {   //if it's not in the cache
                  thumbnail =       //fetch it from the (online?) database (slowest)
                                    // update cache and dictionary
                    }
                }

次のようなものに置き換えられます

      UIImage* thumbnail = [[ImageManager singleton] getImage];

(メインキューに戻ったときにGCDで効果的に提供しているため、完了ブロックは使用しません)

于 2013-01-31T11:40:51.600 に答える
0

JMImageCacheというクラスを使用してイメージをディスクにキャッシュし、ローカル URL (および必要に応じてリモート URL) のみを CoreData に格納しました。また、サムネイルを生成してディスクに保存し、サムネイル URL を画像 URL とともにコア データに保存し、そのサムネイルをテーブル ビューで使用することもできます。

于 2013-01-31T11:34:55.520 に答える