15

シナリオ:

  • ユーザーがボタンをタップして、名簿の変更を要求します。
  • この変更を開始するためのメソッドが呼び出され、アラートビューが表示されます。
  • アラートビューを表示し、UIの応答性を維持するために、dispatch_queueを使用しました。

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                     dispatch_sync(dispatch_get_main_queue(), ^{
                       // Show the alert view
                     });
                   });
    
  • 以下を使用して、名簿の変更プロセスを開始します。

    dispatch_async(modifyingAddressBookQueue, ^{});
    

ここで、いつでも(もちろん、アドレス帳を保存する前に)プロセスをキャンセルできるようにする機能をユーザーに提供したいと思います。そこで、彼がアラートシートのキャンセルボタンをタップしたときに、ディスパッチブロックにアクセスし、特定のBOOLを設定してプロセスを停止し、アドレス帳を元に戻したいと思います。

問題は、それができないということです!すべての変数は一度だけコピーされるため、ブロックにアクセスしてその中の変数を変更することはできません。実行中のブロック内の変数の変更は、ブロックには表示されません。

要約すると、UIイベントを使用して進行中の操作を停止するにはどうすればよいですか?

アップデート:

プロセスのコード:

- (void) startFixingModification {

    _fixContacts = YES;
    __block BOOL cancelled = NO;

    dispatch_queue_t modifyingAddressBookQueue;
    modifyingAddressBookQueue = dispatch_queue_create(sModifyingAddressBookQueueIdentifier,
                                                      NULL);

    dispatch_async(modifyingAddressBookQueue, ^{

        for (NSMutableDictionary *contactDictionary in _contactArray) {

            if (!cancelled) {
                break;
            }

            i = i + 1;

            BOOL didFixContact = [self fixNumberInContactDictionary:contactDictionary];
            if (!didFixContact) {
                _fixedNumbers = _fixedNumbers - 1;
            }

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                dispatch_sync(dispatch_get_main_queue(), ^{
                    [self setAlertViewProgress:i];
                });

            });
        }
    });

    cancelledPtr = &cancelled;

}

alertview(自分のlib)デリゲートのコード

- (void) alertViewProgressCancel:(ASAlertViewProgress *)alertView { // This is a private lib.


    if (cancelledPtr)
    {
        NSLog(@"stopping");

        *cancelledPtr = YES;
    }

}

インターフェイスで、私は宣言します

BOOL*   cancelledPtr;

アップデート2:

本当にイライラします!次のコードの場合

for (NSMutableDictionary *contactDictionary in _contactArray) {

            NSLog(@"%d", _cancelModification);
            if (_cancelModification) {
                break;
            }
}

_cancelModificationがYESに設定されている場合、forループは壊れており、問題ありません。NSLog行をコメントアウトすると、YESに変更されたときに_cancelModificationは無視されます。

4

3 に答える 3

21

BOOLusingを宣言する__blockと、ブロック実行の外部で変更でき、ブロックに新しい値が表示されます。詳細については、ドキュメントを参照してください。

例:

@interface SNViewController ()
{
    BOOL*   cancelledPtr;
}

@end

@implementation SNViewController

- (IBAction)start:(id)sender
{
    __block BOOL cancelled = NO;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (!cancelled) {
            NSLog(@"running");
            sleep(1);
        }        
        NSLog(@"stopped");
    });

    cancelledPtr = &cancelled;
}

- (IBAction)stop:(id)sender
{
    if (cancelledPtr)
    {
        NSLog(@"stopping");

        *cancelledPtr = YES;
    }
}

@end

または、クラスでivarを使用してBOOLを格納します。ブロックは暗黙的にコピーを作成し、selfそれを介してivarにアクセスします。の必要はありません__block

@interface SNViewController ()
{
    BOOL   cancelled;
}

@end

@implementation SNViewController

- (IBAction)start:(id)sender
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (!cancelled) {
            NSLog(@"running");
            sleep(1);
        }        
        NSLog(@"stopped");
    });
}

- (IBAction)stop:(id)sender
{
    NSLog(@"stopping");
    cancelled = YES;
}

@end
于 2012-04-08T22:05:12.563 に答える
7

アプローチ1

「キャンセル可能な」ブロックを返すカスタムdispatch_asyncメソッドを作成します。

// The dispatch_cancel_block_t takes as parameter the "cancel" directive to suspend the block execution or not whenever the block to execute is dispatched. 
// The return value is a boolean indicating if the block has already been executed or not.
typedef BOOL (^dispatch_cancel_block_t)(BOOL cancelBlock);

dispatch_cancel_block_t dispatch_async_with_cancel_block(dispatch_queue_t queue, void (^block)())
{
    __block BOOL execute = YES;
    __block BOOL executed = NO;

    dispatch_cancel_block_t cancelBlock = ^BOOL (BOOL cancelled) {
        execute = !cancelled;
        return executed == NO;
    };

    dispatch_async(queue, ^{
        if (execute)
            block();
        executed = YES;
    });

    return cancelBlock;
}

- (void)testCancelableBlock
{
    dispatch_cancel_block_t cancelBlock = dispatch_async_with_cancel_block(dispatch_get_main_queue(), ^{
        NSLog(@"Block 1 executed");
    });

    // Canceling the block execution
    BOOL success1 = cancelBlock(YES);
    NSLog(@"Block is cancelled successfully: %@", success1?@"YES":@"NO");

    // Resuming the block execution
    // BOOL success2 = cancelBlock(NO);
    // NSLog(@"Block is resumed successfully: %@", success2?@"YES":@"NO");
}

アプローチ2

条件が検証された場合にブロックを非同期で実行するためのマクロを定義する:

#define dispatch_async_if(queue,condition,block) \
dispatch_async(queue, ^{\
    if (condition == YES)\
        block();\
});

- (void)testConditionBlock
{
    // Creating condition variable
    __block BOOL condition = YES;

    dispatch_async_if(dispatch_get_main_queue(), condition, ^{
        NSLog(@"Block 2 executed");
    });

    // Canceling the block execution
    condition = NO;

    // Also, we could use a method to test the condition status
    dispatch_async_if(dispatch_get_main_queue(), ![self mustCancelBlockExecution], ^{
        NSLog(@"Block 3 executed");
    });
}
于 2014-11-05T18:00:08.047 に答える
0

次のコードサンプルを状況に適用してみてください。

__block UIView * tempView = [[UIView alloc] initWithFrame:CGRectMake(50, 100, 220, 30)];
[tempView setBackgroundColor:[UIColor grayColor]];
[self.view addSubview:tempView];
[tempView release];

__block BOOL cancel = NO;
//点击之后就会开始执行这个方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    int i = 0;
    while (i < 1000000000 && cancel == NO) {
        i++;
    }
    NSLog(@"Task end: i = %d", i);
    //这个不会执行,因为在之前,gcd task已经结束
    [tempView removeFromSuperview];
});

//1s 之后执行这个方法
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    NSLog(@"A GCD Task Start");
    cancel = YES;
    [tempView setBackgroundColor:[UIColor blackColor]];
});
于 2013-05-08T11:54:16.290 に答える