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?
source to share
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 ().
source to share
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 >>>");
source to share
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:
bothdidChangeValueForKey:
forisFinished
andisExecuting
And in the class containing the queue
- install
maxConcurrentOperationCount
onNSOperationQueue
on1
- add an operation after all your concurrent actions that will run after they complete.
source to share