シートと長期にわたるタスク
-
09-10-2019 - |
質問
ユーザーがボタンをクリックした後、複雑な(つまり長い)タスクを実行する必要があります。ボタンがシートを開き、Dispatch_asyncおよびその他のグランドセントラルディスパッチのものを使用して長い実行操作が開始されます。
私はコードを書きましたが、それは正常に動作しますが、私がすべてを正しく行ったかどうか、または(私の無知のために)潜在的な問題を無視したかどうかを理解するのに助けが必要です。
ユーザーはボタンをクリックしてシートを開きます。ブロックには長いタスクが含まれています(この例では、(;;)ループのみを実行します。ブロックには、タスクが完了したときにシートを閉じるロジックも含まれます。
-(IBAction)openPanel:(id)sender {
[NSApp beginSheet:panel
modalForWindow:[self window]
modalDelegate:nil
didEndSelector:NULL
contextInfo:nil];
void (^progressBlock)(void);
progressBlock = ^{
running = YES; // this is a instance variable
for (int i = 0; running && i < 1000000; i++) {
[label setStringValue:[NSString stringWithFormat:@"Step %d", i]];
[label setNeedsDisplay: YES];
}
running = NO;
[NSApp endSheet:panel];
[panel orderOut:sender];
};
//Finally, run the block on a different thread.
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue,progressBlock);
}
パネルには、ユーザーが完了する前にタスクを停止できるストップボタンが含まれています
-(IBAction)closePanel:(id)sender {
running = NO;
[NSApp endSheet:panel];
[panel orderOut:sender];
}
解決
このコードには、ステータステキストの値を設定する潜在的な問題があります。基本的に、Appkitのすべてのオブジェクトはメインスレッドからのみ呼び出されることが許可されており、そうでない場合は奇妙な方法で壊れる可能性があります。あなたはそれを呼んでいます setStringValue:
と setNeedsDisplay:
グローバルキューが実行されているスレッドからのラベルのメソッド。これを修正するには、次のようにループを書く必要があります。
for (int i = 0; running && i < 1000000; i++) {
dispatch_async(dispatch_get_main_queue(), ^{
[label setStringValue:[NSString stringWithFormat:@"Step %d", i]];
[label setNeedsDisplay: YES];
});
}
これにより、Appkitが期待するように、メインスレッドからラベルテキストが設定されます。
所属していません StackOverflow