Sean Clark Hess
Sean Clark Hess

Reputation: 16059

Wait for Asynchronous Operations in Objective-C

I'm googling like crazy and still confused about this.

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

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

I'm ok doing them one at a time. I can calculate the total progress easily that way:

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

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

Upvotes: 4

Views: 7382

Answers (3)

Sean Clark Hess
Sean Clark Hess

Reputation: 16059

I just wanted to note that I did get it working by subclassing NSOperation and making it a "concurrent" operation. (Concurrent in this context means an async operation that it should wait for before marking it as complete).

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

Basically, you do the following in your subclass

  1. override start to begin your operation
  2. override isConcurrent to return YES
  3. when you finish, make sure isExecuting and isFinished change to be correct, in a key-value compliant manner (basically, call willChangeValueForKey: and didChangeValueForKey: for isFinished and isExecuting

And in the class containing the queue

  1. set the maxConcurrentOperationCount on the NSOperationQueue to 1
  2. add an operation after all your concurrent ones which will be triggered once they are all done

Upvotes: 0

das
das

Reputation: 3681

dispatch groups are the GCD facility designed to track completion of a set of independent or separately async'd blocks/tasks.

Either use dispatch_group_async() to submit the blocks in question, or dispatch_group_enter() the group before triggering the asynchronous task and dispatch_group_leave() the group when the task has completed.

You can then either get notified asynchronously via dispatch_group_notify() when all blocks/tasks in the group have completed, or if you must, you can synchronously wait for completion with dispatch_group_wait().

Upvotes: 7

Wolfgang Schreurs
Wolfgang Schreurs

Reputation: 11834

I'm not sure if I understand you correctly, but perhaps you need dispatch semaphores to achieve your goal. In one of my projects I use a dispatch semaphore to wait until another turn by another player is completed. This is partially 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 >>>");

Upvotes: 16

Related Questions