1

セルを作成するときに、重い計算を行っています。UITableViewを流動的に保つための最良の方法を見つけようとしていますが、同じタイプでバックグラウンドで計算を行います(あまり処理せずにUIスレッドを維持します)。

テスト目的でのみ、これを重い計算方法として使用しています。

+(NSString*)bigCalculation
{
    int finalValue=0;
    int j=0;
    int i=0;

    for (i=0; i<1000; i++) {
        for (j=0; j<10000000; j++) {
            j++;
        }
        finalValue+=j/100*i;
    }

    return [NSString stringWithFormat:@"%d",finalValue];
}

cellForRowAtIndexPath内では、次のことだけを行っています。

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *identifier=@"identifier";

    UITableViewCell *cell=nil;
    cell=[aTableView dequeueReusableCellWithIdentifier:identifier];
    if(!cell)
    {
        cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
    }

    NSString *text=[dataSource objectForKey:[[dataSource allKeys] objectAtIndex:indexPath.row]];


    dispatch_queue_t a_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
;
    dispatch_async(a_queue, ^{

        NSString *subtitle=[CalculationEngine bigCalculation];
        dispatch_async(dispatch_get_main_queue(), ^{
            [[cell detailTextLabel] setText:subtitle];
            dispatch_release(a_queue); 
        });
    });


    [[cell textLabel] setText:text];
    return cell;
}

現時点では、UITableViewが流動的ですが、バックグラウンドではすべてが正常に機能しています。だから私の質問は:

1)これは私が望むことを達成するための最良の方法ですか、KVOも答えになるでしょうか?

2)これを行う前に:

dispatch_queue_t a_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

やっていた:

dispatch_queue_create("com.mydomain.app.newimagesinbackground", DISPATCH_QUEUE_SERIAL)

そして、パフォーマンスは非常に悪かった。理由を教えていただけますか?

4

2 に答える 2

6

まず、

あなたの実装は根本的に欠陥があります。セルへの参照を、バックグラウンドで実行されるブロックに取り込みます。スクロールしてセルが画面から外れると、セルは再利用プールに入れられます。新しいセルが画面に表示されると、このプールからプルされます。ブロックが完了すると、セルは同じ行に存在しなくなる可能性があります。これを説明するために、作業ブロックの先頭に次のステートメントを配置します。

NSLog(@"my Row is: %d myCell is: %x", indexPath.row, (unsigned int)cell);

その結果:

私の行は:0 myCellは:16fd20
私の行は:13 myCellは:16fd20
私の行は:24 myCellは:16fd20
私の行は:35 myCellは:16fd20
私の行は:44 myCellは:16fd20
私の行は:56 myCellは:16fd20
my Rowは:66 myCellは:16fd20

したがって、ここでは7つのブロックを見ることができます。すべてが異なる行を計算していますが、すべて同じセルを指しています。
ブロックが完了したときにユーザーがスクロールした場合、間違ったセルが更新されます。

dispatch_sync(dispatch_get_main_queue(),0)また、セルを更新するときに使用して、セルがすぐに処理されるようにします。

KVOは、この問題を解決する1つの方法です。


あなたの最初の質問:

KVOはこれを行うための良い方法です。Rob Napierが述べたように、リスト内のアイテムごとに個別のモデルオブジェクトが必要です。tableViewのセルの更新を管理するには、UITableViewCellをサブクラス化します。次に、セルは通知のためにモデルオブジェクトをサブスクライブし、通知が届いたときにそれ自体を更新できます。セルモデルオブジェクトが変更された場合は、古いモデルオブジェクトの通知を辞任し、新しいモデルオブジェクトをサブスクライブするだけです。

これにより、現在のコードで可能なように、セルに不正確な情報が表示されないことが保証されます。

注意点:KVOは、新しい値を設定したのと同じスレッドで通知を送信します。これは、メインスレッドに新しい値を設定するか、observeValueForKeyPath:メソッドのメインスレッドにブロックをディスパッチする必要があることを意味します。


2番目の質問:

この方法でキューを取得する理由:

dispatch_queue_create("com.mydomain.app.newimagesinbackground", DISPATCH_QUEUE_SERIAL);

非常に遅かったので、スケジュールされたブロックごとに1つのシリアルキューを生成しています。これらのキューはすべて同時に実行されます。コアの数が少ないため、キューの実行時間はますます小さくなります。メインキュー(UIを実行する)は単なる別のキューであり、実行に必要な時間も短くなります。小さい。

私のテストでは、キューごとに1つのスレッドがあることがわかりました。セルと同じ数のスレッド。

グローバル並行キューの使用:

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

システムが一度に実行するブロックの数を管理できるようにします。この結果、数百のキューと数百のスレッドの代わりに、1つのキューがあり、私のiPhone4sでは2つのスレッドがあります。コアごとに1つのスレッド。これにより、スケジューリングがはるかに簡単になり、スレッドにより正確な優先順位が割り当てられます。その結果、メインスレッドには実行するのに十分な時間があります。

于 2012-04-14T13:18:37.450 に答える
3

これは良いアプローチではありません。ここでは、計算作業をView Controllerにプッシュすることで、MVCを破っています。データをモデルオブジェクトに保持し、そこで計算を管理する必要があります。テーブルビューにはreloadRowsAtIndexPaths:withRowAnimation:、データが変更されたときに使用して、モデルの現在の値が表示されます。KVOは、データがいつ変更されるかを判断するための合理的な方法の1つです。

計算を怠惰にしたい場合は、モデルを呼び出して、recalculateIfNeeded誰か(この場合はテーブルビュー)が新しい値を要求していることをモデルに通知できます。ただし、入力データが実際に変更されていない場合は、再計算しないでください。モデルは、データが変更されたかどうかを追跡する場所です。

于 2012-04-11T17:42:29.553 に答える