0

次のようなボタンを押すと発生する無害なループがある場合:

- (IBAction)start:(id)sender{
    calculatingProgressBar.progress = 0.0;  
    int j = 1000;
    for (int i=0; i<j; i++) {
        calculatingProgressBar.progress = (i/j);
    }
}

ProgressView は更新されません。他の言語では、これらの状況で実行する必要があるビュー更新コマンドがあります。Cocoaに相当するものは何ですか、または他の回避策を実行する必要がありますか.

4

4 に答える 4

4

問題は、実行(イベント)ループが回転するときにUIが定期的にのみ更新されることです。メインスレッドで実行されると想定しているfor()ループは、終了するまで実行ループをブロックします。これは、プログレスバーが更新されない(つまり、最後に100%にジャンプする)ことを意味するだけでなく、しばらくの間UIをフリーズする可能性があることを意味します。(プログレスバーを表示しているので、これにはしばらく時間がかかると思います。)

長時間の操作の場合は、別のキュー/スレッドで作業を行い、メインスレッドのUIの更新を定期的に呼び出す必要があります。たとえば、次のように試すことができます(警告:ブラウザに入力):

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
    int j = 1000;
    for (int i=0; i<j; i++) {
        // Do your presumably useful work, not just looping for fun :-)
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI updates always come from the main queue!
            calculatingProgressBar.progress = (i/j);
        });
    }
});

この例では、GCD(Grand Central Dispatch)を使用しています。NSOperationQueueのように、より高いレベルの構造も見ることができますが、基本的な考え方は同じです。

並行(マルチスレッド)プログラミングに慣れていない場合は、習熟するための適度な学習曲線が必要になります。並行性プログラミングガイドから始めることをお勧めします。

于 2012-05-15T22:45:20.037 に答える
1

メインスレッドでコードを実行している間、UIはそれ自体を更新できません。これは意図的なものであり、パフォーマンスが大幅に向上します。つまり、start戻るまでUIは更新されません。何度も更新progressしてから戻ってくるので、一度描画されます。

あなたが話していることを達成するために、タイマーまたはGCDキューを使用する必要があります。コードの残りの部分がどのように見えるかにかなり依存しますが、たとえば60秒以上実行したい場合は、次の1つのアプローチを使用します。

- (IBAction)start:(id)sender{
  calculatingProgressBar.progress = 0.0;  
  int j = 60;

  dispatch_time_t now = dispatch_time(DISPATCH_TIME_NOW, 0);
  for (int i=0; i<j; i++) {
    dispatch_after(dispatch_time(now, i * NSEC_PER_SEC),
                   dispatch_get_current_queue(),
                   ^{
                     calculatingProgressBar.progress = (i/j);
                    });
    }
}

これは、60のイベントを設定し、1秒間隔で実行するようにスケジュールします。その後、(まだ実行せずに)戻ります。

NSTimerまたはGCDタイマーなど、他にも多くのアプローチがあります。を使用して、処理をバックグラウンドスレッドに移動することもできますdispatch_async()。(そのためのコンラッドシュルツの答えを参照してください)

于 2012-05-15T22:46:12.397 に答える
0

よりエレガントな(そして用途の広い)解決策はこれだと思います:あなたの代表者にこれを持ってください:

maxNumber = 1000; // define this somewhere, just for convenience
...
[self performSelectorInBackground:@selector(doLoop:) withObject:[NSNumber numberWithInt:maxNumber]];

次のような方法があります。

- (void)doLoop:(NSNumber *)myMaxNumber { // this runs on the background
  calculatingProgressBar.progress = 0.0;  
  NSNumber *j = myMaxNumber;
  for (int i=0; i<j; i++) {
    [self performSelectorOnMainThread:@selector(updateProgress:) withObject:[NSNumber numberWithInt:i] waitUntilDone:NO];
  }
  // here you could call a method in the main thread, just to signify that your loop is done.
  // [self performSelectorOnMainThread:@selector(loopDone:) withObject:nil waitUntilDone:NO];
}

更新はここで行われます:

-(void)updateProgress:(NSNumber *)myNumber {
  calculatingProgressBar.progress = ([myNumber intValue]/maxNumber);
}
于 2012-12-10T19:03:44.520 に答える
-1

calculateProgressBar が IBOutlet であると仮定すると、おそらく KVO に準拠した方法で新しい値を設定する必要があります。つまり、setter を使用します。

于 2012-05-15T22:35:12.463 に答える