1

これが問題です: サムネイルを Core Data に保存してフェッチした後、テーブルビューを更新してから、セル自体を更新するように指示します。これにより、画像が Core Data にロードされたときにサムネイルを表示できます。Core Dataはスレッドセーフではなく、もちろんすべてのGUI要素がメインスレッドで発生する必要があるため、2つの異なるスレッドを使用します。

しかし、このメソッド全体が永遠にループし続けるだけで、原因はスレッドをリロードしたときです。

[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

なんで?どうすればこれを修正できますか?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Photo"];
    Photo *photo = [self.fetchedResultsController objectAtIndexPath:indexPath];    
    cell.textLabel.text = photo.title;
    cell.detailTextLabel.text = photo.subtitle;

    NSLog(@"Context %@", self.photographer.managedObjectContext);
    [self.photographer.managedObjectContext performBlock:^{
        [Photo setThumbnailForPhoto:photo];
        dispatch_async(dispatch_get_main_queue(), ^{
            cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
        });
    }];
    return cell;
}
4

3 に答える 3

3

無限ループはcellForRowAtIndexPath:、フェッチがロードされ、特定のセルでリロードが呼び出された後に再度呼び出すことによって発生します。

リロードすると、cellForRowAtIndexPath:が再度呼び出されます...そしてあなたの場合、何度も何度も無限に呼び出されます。

解決策は簡単です... cellForRowAtIndexPath ではなく、fetchrequest のコールバック メソッドでセルをリロードしないでください。次に、セルを作成するのではなく、そこにリロードします。

むしろ、イメージをまったくロードしないでくださいcellForRowAtIndexpath:。テーブルがインスタンス化されるたびに、データソースをループして各アイテムのそれぞれのセルを取得するメソッドを作成します。次に、必要と思われる各セルの画像を読み込みます。また、アイテムのフェッチが完了するたびにセルをリロードします (たとえば、コールバック メソッド)。

今行ったように、セルの作成内に画像をロードしたい場合(ただし、それが適切な方法だとは思いません)。イメージが既に設定されているかどうかをチェックする if ステートメントで performBlock: 全体を囲むことができます。

于 2013-03-20T15:14:57.373 に答える
1

他の人がすでに言ったようreloadRowsAtIndexPathsに、画像が読み込まれたときに呼び出す必要はありません。に新しいイメージを割り当てるcell.imageView.imageだけで十分です。

しかし、あなたのコードには別の問題があります。self.photographer.managedObjectContextこれは「プライベート同時実行タイプ」のマネージド オブジェクト コンテキストであると想定しているためperformBlock、バックグラウンド スレッドで実行されます。(それ以外の場合は、使用する必要はありませんdispatch_async(dispatch_get_main_queue(), ...)。)

そのperformBlock:ため、バックグラウンド スレッドでコードを非同期的に実行します。(UI がブロックされないため、これは良いことです。) しかし、しばらくしてから画像がフェッチされると、セルが別の行に再利用された可能性があります。(これは、イメージのフェッチ中に行が非表示になるようにテーブル ビューをスクロールした場合に発生します。)

したがって、セルがまだテーブルビューの同じ位置にあるかどうかを確認する必要があります。

[self.photographer.managedObjectContext performBlock:^{
    [Photo setThumbnailForPhoto:photo];
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([[tableView indexPathForCell:cell] isEqualTo:indexPath]) {
            cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
        }
    });
}];
于 2013-03-20T15:47:25.547 に答える
1

次のコードを置き換えます。

    cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

と:

    UITableViewCell *blockCell = [tableView cellForRowAtIndexPath:indexPath];
    blockCell.imageView.image = [UIImage imageWithData:photo.thumbnail];
    [blockCell setNeedsLayout];

これにより、再帰的なリロードの問題とcell、その間に再利用される可能性が解決されます。また、写真データを既に読み込んでいるかどうかも確認し、読み込んでいる場合は更新しません。

ただし、使用しているためNSFetchedResultsController、写真データが更新されたときにデリゲートのコールバックが既に通知されている場合は、Totomus Maximus の回答を使用することをお勧めします。この方法は画像ビューを更新するだけで、Photo update メソッドが変更できる他の情報は更新しません。

于 2013-03-20T16:05:34.867 に答える