Waiting for Asynchronous Operations in Objective-C

I am going crazy like crazy and am still confused about it.

I want to load an array of urls files to disk and I want to update my view based on the bytes loaded by each file as they are loaded. I already have something that will download the file and report progress and completion through blocks.

How can I do this for each file in the array?

I'm fine doing them one at a time. I can easily calculate the overall progress this way:

float progress = (numCompletedFiles + (currentDownloadedBytes / currentTotalBytes)) / totalFiles)

      

I mostly understand GCD and NSOperations, but how can you tell that an operation or dispatch_async block is waiting until the callback is called? It seems possible by overriding NSOperation, but it seems like overkill. Is there another way? Is this possible with just GCD?

+3


source to share


3 answers


dispatch groups are a GCD tool for tracking the completion of a set of independent or separately asynchronous blocks / tasks.

Either use dispatch_group_async () to dispatch the appropriate blocks or dispatch_group_enter () to the group before starting the asynchronous task and dispatch_group_leave () to the group when the task is complete.



Then you can either receive an asynchronous notification via dispatch_group_notify () when all blocks / tasks in the group have completed, or if you must, you can synchronously wait for completion using dispatch_group_wait ().

+7


source


I'm not sure if I understand you correctly, but maybe you need to send semaphores to achieve your goal. In one of my projects, I use a dispatch semaphore to wait until another turn of another player completes. This is part of the code I used.



for (int i = 0; i < _players.count; i++)
{

    // a semaphore is used to prevent execution until the asynchronous task is completed ...

    dispatch_semaphore_t sema = dispatch_semaphore_create(0);


    // player chooses a card - once card is chosen, animate choice by moving card to center of board ...

    [self.currentPlayer playCardWithPlayedCards:_currentTrick.cards trumpSuit:_trumpSuit completionHandler:^ (WSCard *card) {

        BOOL success = [self.currentTrick addCard:card];

        DLog(@"did add card to trick? %@", success ? @"YES" : @"NO");

        NSString *message = [NSString stringWithFormat:@"Card played by %@", _currentPlayer.name];
        [_messageView setMessage:message];

        [self turnCard:card];
        [self moveCardToCenter:card];


        // send a signal that indicates that this asynchronous task is completed ...

        dispatch_semaphore_signal(sema);

        DLog(@"<<< signal dispatched >>>");
    }];


    // execution is halted, until a signal is received from another thread ...

    DLog(@"<<< wait for signal >>>");

    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    dispatch_release(sema);


    DLog(@"<<< signal received >>>");

      

+16


source


I just wanted to point out that I got it working by subclassing NSOperation and making it a "parallel" operation. (Parallel in this context means an async operation that it must wait before marking it complete.)

http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/

Basically you do the following in your subclass

  • override start

    to start your work.
  • override isConcurrent

    to returnYES

  • when you're done, make sure the value of isExecuting and isFinished is correct, given a match to the key's value (basically, call willChangeValueForKey:

    both didChangeValueForKey:

    for isFinished

    andisExecuting

And in the class containing the queue

  • install maxConcurrentOperationCount

    on NSOperationQueue

    on1

  • add an operation after all your concurrent actions that will run after they complete.
0


source







All Articles