Waiting for multiple blocks to finish - ios

I have those methods to retrieve some object information from the internet:
- (void)downloadAppInfo:(void(^)())success
failure:(void(^)(NSError *error))failure;
- (void)getAvailableHosts:(void(^)())success
failure:(void(^)(NSError *error))failure;
- (void)getAvailableServices:(void(^)())success
failure:(void(^)(NSError *error))failure;
- (void)getAvailableActions:(void(^)())success
failure:(void(^)(NSError *error))failure;
The downloaded stuff gets stored in object properties, so that is why the success functions return nothing.
Now, I want to have one method like this:
- (void)syncEverything:(void(^)())success
failure:(void(^)(NSError *error))failure;
Which does nothing else than calling all the methods above, and returning only after every single method has performed its success or failure block.
How can I do this?
Hint: I am aware that cascading the methods calls in each others success block would work. But this is neither 'clean' nor helpful when later implementations include further methods.
Attempts:
I tried running each of the calls in an NSOperation and adding those NSOperations to an NSOperationQueue followed by a "completion operation" which depends on every one of the preceding operations.
This won't work. Since the operations are considered completed even before their respective success/failure blocks return.
I also tried using dispatch_group. But it is not clear to me wether I am doing it the right way. Unfortunately, it is not working.

Drawn from the comments in other answers here, and the blog post Using dispatch groups to wait for multiple web services, I arrived at the following answer.
This solution uses dispatch_group_enter and dispatch_group_leave to determine when each intermediate task is running. When all tasks have finished, the final dispatch_group_notify block is called. You can then call your completion block, knowing that all intermediate tasks have finished.
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self yourBlockTaskWithCompletion:^(NSString *blockString) {
// ...
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self yourBlockTaskWithCompletion:^(NSString *blockString) {
// ...
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
// All group blocks have now completed
if (completion) {
completion();
}
});
Grand Central Dispatch - Dispatch Groups
https://developer.apple.com/documentation/dispatch/dispatchgroup
Grouping blocks allows for aggregate synchronization. Your application can submit multiple blocks and track when they all complete, even though they might run on different queues. This behavior can be helpful when progress can’t be made until all of the specified tasks are complete.
Xcode Snippet:
I find myself using Dispatch Groups enough that I've added the following code as an Xcode Snippet for easy insertion into my code.
Now I type DISPATCH_SET and the following code is inserted. You then copy and paste an enter/leave for each of your async blocks.
Objective-C:
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_leave(group);
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
});
Swift:
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
dispatchGroup.leave()
dispatchGroup.notify(queue: .global()) {
}

You were almost there, the problem is most likely to be that those methods are asynchronous, so you need an extra synchronization step. Just try with the following fix:
for(Appliance *appliance in _mutAppliances) {
dispatch_group_async(
group,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t sem = dispatch_semaphore_create( 0 );
NSLog(#"Block START");
[appliance downloadAppInfo:^{
NSLog(#"Block SUCCESS");
dispatch_semaphore_signal(sem);
}
failure:^(NSError *error){
NSLog(#"Block FAILURE");
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(#"Block END");
});
dispatch_group_notify(
group,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSLog(#"FINAL block");
success();
});
}

One other solution is to use a Promise which is available in a few third party libraries.
I'm the author of RXPromise, which implements the Promises/A+ specification.
But there are at least two other Objective-C implementations.
A Promise represents the eventual result of an asynchronous method or operation:
-(Promise*) doSomethingAsync;
The promise is a complete replacement for the completion handler. Additionally, due to its clear specification and underlaying design, it has some very useful features which make it especially easy to handle rather complex asynchronous problems.
What you need to do first, is to wrap your asynchronous methods with completion handlers into asynchronous methods returning a Promise:
(Purposefully, your methods return the eventual result and a potential error in a more convenient completion handler)
For example:
- (RXPromise*) downloadAppInfo {
RXPromise* promise = [RXPromise new];
[self downloadAppInfoWithCompletion:^(id result, NSError *error) {
if (error) {
[promise rejectWithReason:error];
}
else {
[promise fulfillWithValue:result];
}
}];
return promise;
}
Here, the original asynchronous method becomes the "resolver" of the promise. A promise can be either fulfilled (success) or rejected (failure) with either specifying the eventual result of the task or the reason of the failure. The promise will then hold the eventual result of the asynchronous operation or method.
Note that the wrapper is an asynchronous method, which returns immediately a promise in a "pending" state.
Finally, you obtain the eventual result by "registering" a success and a failure handler with a then method or property. The few promise libraries around do differ slightly, but basically it may look as follows:
`promise.then( <success-handler>, <error-handler> )`
The Promise/A+ Specification has a minimalistic API. And the above is basically ALL one need for implementing the Promise/A+ spec - and is often sufficient in many simple use cases.
However, sometimes you need bit more - for example the OPs problem, which require to "wait" on a set of asynchronous methods and then do something when all have completed.
Fortunately, the Promise is an ideal basic building block to construct more sophisticated helper methods quite easily.
Many Promise libraries provide utility methods. So for example a method all (or similar) which is an asynchronous method returning a Promise and taking an array of promises as input. The returned promise will be resolved when all operations have been completed, or when one fails. It may look as follows:
First construct an array of promises, and simultaneously starting all asynchronous tasks in parallel:
NSArray* tasks = #[
[self downloadAppInfo],
[self getAvailableHosts],
[self getAvailableServices],
[self getAvailableActions],
];
Note: here, the tasks are already running (and may complete)!
Now, use a helper method which does exactly what stated above:
RXPromise* finalPromise = [RXPromise all:tasks];
Obtain the final results:
finalPromise.then(^id( results){
[self doSomethingWithAppInfo:results[0]
availableHosts:results[1]
availableServices:results[2]
availableActions:results[3]];
return nil;
}, ^id(NSError* error) {
NSLog(#"Error %#", error); // some async task failed - log the error
});
Note that either the success or the failure handler will be called when the returned promise will be resolved somehow in the all: method.
The returned promise (finalPromise) will be resolved, when
all tasks succeeded successfully, or when
one task failed
For case 1) the final promise will be resolved with an array which contains the result for each corresponding asynchronous task.
In case 2) the final promise will be resolved with the error of the failing asynchronous task.
(Note: the few available libraries may differ here)
The RXPromise library has some additional features:
Sophisticated cancellation which forwards a cancellation signal in the acyclic graph of promises.
A way to specify a dispatch queue where the handler will run. The queue can be used to synchronize access to shared resources for example, e.g.
self.usersPromise = [self fetchUsers];
self.usersPromise.thenOn(dispatch_get_main_queue(), ^id(id users) {
self.users = users;
[self.tableView reloadData];
}, nil);
When compared to other approaches, the dispatch_group solution suffers from the fact that it blocks a thread. This is not quite "asynchronous". It's also quite complex if not impossible to implement cancellation.
The NSOperation solution appears to be a mixed blessing. It may be elegant only if you already have NSOperations, and if you have no completion handlers which you need to take into account when defining the dependencies - otherwise, it becomes cluttered and elaborated.
Another solution, not mentioned so far, is Reactive Cocoa. IMHO, it's an awesome library which lets you solve asynchronous problems of virtually any complexity. However, it has a quite steep learning curve, and may add a lot of code to your app. And I guess, 90% of asynchronous problems you stumble over can be solved with cancelable promises. If you have even more complex problems, so take a look at RAC.

If you want to create a block based solution you could do something like
- (void)syncEverything:(void(^)())success failure:(void(^)(NSError *error))failure
{
__block int numBlocks = 4;
__block BOOL alreadyFailed = NO;
void (^subSuccess)(void) = ^(){
numBlocks-=1;
if ( numBlocks==0 ) {
success();
}
};
void (^subFailure)(NSError*) = ^(NSError* error){
if ( !alreadyFailed ) {
alreadyFailed = YES;
failure(error);
}
};
[self downloadAppInfo:subSuccess failure:subFailure];
[self getAvailableHosts:subSuccess failure:subFailure];
[self getAvailableServices:subSuccess failure:subFailure];
[self getAvailableActions:subSuccess failure:subFailure];
}
It's kind of quick and dirty, and you might need to do block copys. If more than one method fails, you will only get one overall failure.

Here is my solution without any dispatch_group.
+(void)doStuffWithCompletion:(void (^)(void))completion{
__block NSInteger stuffRemaining = 3;
void (^dataCompletionBlock)(void) = ^void(void) {
stuffRemaining--;
if (!stuffRemaining) {
completion();
}
};
for (NSInteger i = stuffRemaining-1; i > 0; i--) {
[self doOtherStuffWithParams:nil completion:^() {
dataCompletionBlock();
}];
}
}

Related

Cancellable set of asynchronous operations with progress reporting in iOS

Suppose that I use another SDK (which I do not have control of) with an API that imports 1 file asynchronously, and calls a completion callback on completion.
The following is an example API.
func importFile(filePath: String, completion: () -> Void)
I need to import 10 files (one by one) using this API, but I need it to be cancellable, e.g. after Files 1,2,3 has been successfully imported, while File 4 is being imported, I want to be able to cancel the whole set of operations (import of the 10 Files), such that File 4 will finish (since it already started), but Files 5-10 will not be imported anymore.
In addition, I also need to report progress of the import. When File 1 has been imported successfully, then I should report progress of 10% (1 out of 10 has been finished).
How can I achieve this?
I am considering using NSOperationQueue with 10 NSOperations, but it seems that progress reporting will be difficult.
So, I believe that this is the following you want from your question:
Import n files one by one in a queue
Report progress when each file is imported
Ability to cancel operation in the middle
It can be achieved using NSOperationQueue and NSBlockOperation in the following way.
Create AsyncBlockOperation and NSOperationQueue+AsyncBlockOperation classes from code given in answer for the following StackOverflow question: NSOperation wait until asynchronous block executes
Import both the classes into the file they are going to be used
Create an operation queue
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 1;
operationQueue.name = #"com.yourOrganization.yourProject.yourQueue";
Create a function which gives you a callback for getting progress
- (void)importFilesFromFilePathArray:(NSArray *)pathsArray
inOperationQueue:(NSOperationQueue *)operationQueue
withProgress:(void (^)(CGFloat progress))progressBlock {
}
Inside the function defined in 2, use NSBlockOperation for performing your operations in the NSOperationQueue
for (int i = 0; i < pathsArray.count; i++) {
[operationQueue addAsyncOperationWithBlock:^(dispatch_block_t completionHandler) {
[self importFile:(NSString *)[pathsArray objectAtIndex:i] completion:^(bool completion) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
CGFloat progress = (100 * (float)(i+1)/pathsArray.count);
progressBlock(progress);
if (progress == 100) {
successBlock(YES);
}
}];
completionHandler();
}];
}];
}
For cancelling operations, we can simply use the operationQueue that we created in the 1st step
[operationQueue cancelAllOperations];
I tried this code myself. It's working well. Feel free to suggest improvements to make it better :)
I think you should add dependencies on your operation.
The idea is this:
1Op = NSOperation..
2Op = NSOperation..
.
.
10Op = NSOperation..
10Op.addDependency(9Op)
9Op.addDependency(8Op) and so on...
Then your operation subclass should override the cancel method in this way
cancel() {
super.cancel()
for dep in dependencies {
dep.cancel()
}
}
NSOperationQueue offers a nice object oriented abstaraction and is the way I would go.
Create a NSOperationQueuenamed importQueue
For each import:
Create a NSOperation
Store the operation to reach and possibly cancle it later on, for instance in a dictionary [ImportNumber:NSOperation]
Add the operation to importQueue
In regards to the progress state:
Option 1:
NSOperationQueue has a property called operations, whichs count you are able to observe. (importQueue.operations.count).
Option 2:
NSOperation offers completion blocks. You can increase a counter when queueing the operation and decrease it within the completion block or when cancelling.
Further reading: Apple documentation, asynchronous-nsoperation-why-and-how, nice but oldish tutorial.

Implementing Critical section using GCD in iOS

I have a really heavy task to perform, and I dont want to block the main thread. So I'm starting a separate Concurrent queue for it. There can be 4 instances of the said task.
-(dispatch_queue_t)getConcurrentQueue
{
if(concurrentQueue == nil)
{
concurrentQueue = dispatch_queue_create("com.myself.HeavyTask", DISPATCH_QUEUE_CONCURRENT);
}
return concurrentQueue;
}
Now to start the heavy task I have -
-(void)beginTask
{
//.....
//.....
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = [self getConcurrentQueue];
dispatch_async(queue, ^{
[weakSelf heavyTask];
});
}
Now the method heavyTask goes like this --
-(void)heavyTask
{
//...
dispatch_sync(dispatch_get_current_queue(), ^{
// Initialising code for heavy task
// This is the critical section. Only one of the 4 concurrent threads can enter this at a time
}
//....
while(condition)
{
// Perform meat of the task
}
//...
dispatch_sync(dispatch_get_current_queue(), ^{
// Teardown code. Freeing memory etc.
// This is also a critical section.
}
//...
}
The initialising code and teardown code uses some 3rd party C methods which are not thread safe. So making them thread safe is not the scope of the question.
Now I've put the "initialising code" and and the "teardown code" within -
dispatch_sync(dispatch_get_current_queue(), ^{
}
My code is crashing and I'm getting error messages stating that there is insufficient thread locking around the critical section code.
I read that dispatch_get_current_queue() is not safe, so I replaced it with concurrentQueue. I also tried replacing with dispatch_get_main_queue(). Still the code crashes, complaining about insufficient thread locking.
I know there is something wrong in my understanding of implementing critical section using GCD.
Can anyone clearly show me how to make my code work properly here??
Side question -- Can I use #synchronized { } blocks here??
There's a lot wrong with your code, including not adhering to naming conventions.
So basically, if you want to execute the same task concurrently with respect to each other, use the global concurrent queue to execute those tasks.
If you want to concurrently access shared resources from within these tasks (or from elsewhere), define a dedicated queue, say "sync_queue" where you exclusively access these resources. This "sync_queue" executes your "critical sections".
The "sync_queue" can be serial or concurrent.
If you use a serial queue, use dispatch_async(sync_queue, block) for write access and dispatch_sync(sync_queue, block) for read access to shared resources.
If you use a concurrent queue, use dispatch_barrier_async(sync_queue, block) for write access and dispatch_barrier_sync(sync_queue, block) for read access to shared resources.
Example:
// Read access using a serial sync_queue:
...
__block int counter;
dispatch_sync(sync_queue, ^{
counter = _counter;
});
// Write access using a serial sync_queue:
...
dispatch_async(sync_queue, ^{
_counter = counter;
});
// Read access using a concurrent sync_queue:
...
__block int counter;
dispatch_barrier_sync(sync_queue, ^{
counter = _counter;
});
// Write access using a concurrent sync_queue:
...
dispatch_barrier_async(sync_queue, ^{
_counter = counter;
});
Example for your "heavy task":
-(void)heavyTask
{
dispatch_barrier_async(sync_queue, ^{
// Initialize heavy task
...
// Continue with the task:
dispatch_async(dispatch_get_global_queue(0,0), ^{
BOOL condition = YES; // condition must be local to the block (it's not a shared resource!)
while(condition)
{
// Perform meat of the task
condition = ...;
}
dispatch_barrier_async(sync_queue, ^{
// Teardown code. Freeing memory etc.
// This is also a critical section.
...
}
});
}
}
You called it "getSerialQueue" but really you are creating a "concurrent" queue in it. Try to fix it substituting DISPATCH_QUEUE_CONCURRENT with DISPATCH_QUEUE_SERIAL in getSerialQueue.
Keep in mind that:
dispatch_sync means: I will wait here until this block finishes
dispatch_async means: I will not wait
This is not related to concurrent or serial. If two tasks in a concurrent queue call dispatch_sync(block), 'block' will be executed concurrently.
Hope this helps.

Serializing asynchronous tasks in objective C

I wanted to be able to serialize 'genuinely' async methods, for example:
making a web request
showing a UIAlertView
This is typically a tricky business and most samples of serial queues show a 'sleep' in an NSBlockOperation's block. This doesn't work, because the operation is only complete when the callback happens.
I've had a go at implementing this by subclassing NSOperation, here's the most interesting bits of the implementation:
+ (MYOperation *)operationWithBlock:(CompleteBlock)block
{
MYOperation *operation = [[MYOperation alloc] init];
operation.block = block;
return operation;
}
- (void)start
{
[self willChangeValueForKey:#"isExecuting"];
self.executing = YES;
[self didChangeValueForKey:#"isExecuting"];
if (self.block) {
self.block(self);
}
}
- (void)finish
{
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
self.executing = NO;
self.finished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
}
- (BOOL)isFinished
{
return self.finished;
}
- (BOOL) isExecuting
{
return self.executing;
}
This works well, here's a demonstration...
NSOperationQueue *q = [[NSOperationQueue alloc] init];
q.maxConcurrentOperationCount = 1;
dispatch_queue_t queue = dispatch_queue_create("1", NULL);
dispatch_queue_t queue2 = dispatch_queue_create("2", NULL);
MYOperation *op = [MYOperation operationWithBlock:^(MYOperation *o) {
NSLog(#"1...");
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(#"1");
[o finish]; // this signals we're done
});
}];
MYOperation *op2 = [MYOperation operationWithBlock:^(MYOperation *o) {
NSLog(#"2...");
dispatch_async(queue2, ^{
[NSThread sleepForTimeInterval:2];
NSLog(#"2");
[o finish]; // this signals we're done
});
}];
[q addOperations:#[op, op2] waitUntilFinished:YES];
[NSThread sleepForTimeInterval:5];
Note, I also used a sleep but made sure these were executing in background thread to simulate a network call. The log reads as follows
1...
1
2...
2
Which is as desired. What is wrong with this approach? Are there any caveats I should be aware of?
"Serializing" asynchronous tasks will be named actually "continuation" (see also this wiki article Continuation.
Suppose, your tasks can be defined as an asynchronous function/method with a completion handler whose parameter is the eventual result of the asynchronous task, e.g.:
typedef void(^completion_handler_t)(id result);
-(void) webRequestWithCompletion:(completion_handler_t)completionHandler;
-(void) showAlertViewWithResult:(id)result completion:(completion_handler_t)completionHandler;
Having blocks available, a "continuation" can be easily accomplished through invoking the next asynchronous task from within the previous task's completion block:
- (void) foo
{
[self webRequestWithCompletion:^(id result) {
[self showAlertViewWithResult:result completion:^(id userAnswer) {
NSLog(#"User answered with: %#", userAnswer);
}
}
}
Note that method foo gets "infected by "asynchrony" ;)
That is, here the eventual effect of the method foo, namely printing the user's answer to the console, is in fact again asynchronous.
However, "chaining" multiple asynchronous tasks, that is, "continuing" multiple asynchronous tasks, may become quickly unwieldy:
Implementing "continuation" with completion blocks will increment the indentation for each task's completion handler. Furthermore, implementing a means to let the user cancel the tasks at any state, and also implement code to handle the error conditions, the code gets quickly confusing, difficult to write and difficult to understand.
A better approach to implement "continuation", as well as cancellation and error handling, is using a concept of Futures or Promises. A Future or Promise represents the eventual result of the asynchronous task. Basically, this is just a different approach to "signal the eventual result" to the call site.
In Objective-C a "Promise" can be implemented as an ordinary class. There are third party libraries which implement a "Promise". The following code is using a particular implementation, RXPromise.
When utilizing such a Promise, you would define your tasks as follows:
-(Promise*) webRequestWithCompletion;
-(Promise*) showAlertViewWithResult:(id)result;
Note: there is no completion handler.
With a Promise, the "result" of the asynchronous task will be obtained via a "success" or an "error" handler which will be "registered" with a then property of the promise. Either the success or the error handler gets called by the task when it completes: when it finishes successfully, the success handler will be called passing its result to the parameter result of the success handler. Otherwise, when the task fails, it passes the reason to the error handler - usually an NSError object.
The basic usage of a Promise is as follows:
Promise* promise = [self asyncTasks];
// register handler blocks with "then":
Promise* handlerPromise = promise.then( <success handler block>, <error handler block> );
The success handler block has a parameter result of type id. The error handler block has a parameter of type NSError.
Note that the statement promise.then(...) returns itself a promise which represents the result of either handler, which get called when the "parent" promise has been resolved with either success or error. A handler's return value may be either an "immediate result" (some object) or an "eventual result" - represented as a Promise object.
A commented sample of the OP's problem is shown in the following code snippet (including sophisticated error handling):
- (void) foo
{
[self webRequestWithCompletion] // returns a "Promise" object which has a property "then"
// when the task finished, then:
.then(^id(id result) {
// on succeess:
// param "result" is the result of method "webRequestWithCompletion"
return [self showAlertViewWithResult:result]; // note: returns a promise
}, nil /*error handler not defined, fall through to the next defined error handler */ )
// when either of the previous handler finished, then:
.then(^id(id userAnswer) {
NSLog(#"User answered with: %#", userAnswer);
return nil; // handler's result not used, thus nil.
}, nil)
// when either of the previous handler finished, then:
.then(nil /*success handler not defined*/,
^id(NEError* error) {
// on error
// Error handler. Last error handler catches all errors.
// That is, either a web request error or perhaps the user cancelled (which results in rejecting the promise with a "User Cancelled" error)
return nil; // result of this error handler not used anywhere.
});
}
The code certainly requires more explanation. For a detailed and a more comprehensive description, and how one can accomplish cancellation at any point in time, you may take a look at the RXPromise library - an Objective-C class which implements a "Promise". Disclosure: I'm the author of RXPromise library.
At a first glance this would work, some parts are missing to have a "proper" NSOperation subclass though.
You do not cope with the 'cancelled' state, you should check isCancelled in start, and not start if this returns YES ("responding to the cancel command")
And the isConcurrent method needs to be overridden too, but maybe you omitted that for brevity.
When subclassing NSOperation I would strongly suggest only overriding main unless you really know what you are doing as it is really easy to mess up thread safety. While the documentation says that the operation will not be concurrent the act of running them through an NSOperationQueue automatically makes them concurrent by running them on a separate thread. The non-concurrency note only applies if you call the start method of the NSOperation yourself. You can verify this by noting the thread ID that each NSLog line contains. For example:
2013-09-17 22:49:07.779 AppNameGoesHere[58156:ThreadIDGoesHere] Your log message goes here.
The benefit of overriding main means that you don't have to deal with thread safety when changing the state of the operation NSOperation handles all of that for you. The main thing that is serializing your code is the line that sets maxConcurrentOperationCount to 1. This means each operation in the queue will wait for the next to run (all of them will run on a random thread as determined by the NSOperationQueue). The act of calling dispatch_async inside each operation also triggers yet another thread.
If you are dead set on using subclassing NSOperation then only override main, otherwise I would suggest using NSBlockOperation which seems like what you are somewhat replicating here. Really though I would avoid NSOperation altogether, the API is starting to show its age and is very easy to get wrong. As an alternative I would suggest something like RXPromise or my own attempt at solving this problem, FranticApparatus.

How can I wait for a NSURLConnection delegate to finish before executing the next statement?

This has been a hard one to search.
I found a similar question, iOS 5 Wait for delegate to finish before populating a table?, but the accepted answer was 'Refresh the table view,' and that does not help me. The other results I found tended to be in c#.
I have an app that streams from iPhone to Wowza servers. When the user hits record, I generate a unique device id, then send it to a PHP script on the server that returns a JSON document with configuration settings (which includes the rtmp dump link).
The problem is, the delegate methods are asynchronous, but I need to get the config settings before the next lines of code in my - (IBAction)recordButtonPressed method, since that code is what sets the profile settings, and then records based on those settings.
I've realized I could make the NSURLConnection in -recordButtonPressed like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading (or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
Is there not some simple waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag flag I can send to the delegator so I can have sequential operations that pull data from the web?
I've realized I could make the NSURLConnection in -recordButtonPressed like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading (or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
You have analyzed and understood the situation and you have described its possible solutions perfectly. I just don't agree with your conclusions. This kind of thing happens all the time:
- (void) doPart1 {
// do something here that will eventually cause part2 to be called
}
- (void) doPart2 {
}
You can play various games with invocations to make this more elegant and universal, but my advice would be, don't fight the framework, as what you're describing is exactly the nature of being asynchronous. (And do not use a synchronous request on the main thread, since that blocks the main thread, which is a no-no.)
Indeed, in an event-driven framework, the very notion "wait until" is anathema.
Why not to use synchronous request?
Wrap your asynchronous NSURLConnection request in a helper method which has a completion block as a parameter:
-(void) asyncDoSomething:(void(^)(id result)completionHandler ;
This method should be implemented in the NSURLConnectionDelegate. For details see the example implementation and comments below.
Elsewhere, in your action method:
Set the completion handler. The block will dispatch further on the main thread, and then perform anything appropriate to update the table data, unless the result was an error, in which case you should display an alert.
- (IBAction) recordButtonPressed
{
[someController asyncConnectionRequst:^(id result){
if (![result isKindOfClass:[NSError class]]) {
dispatch_async(dispatch_get_main_queue(), ^{
// We are on the main thread!
someController.tableData = result;
});
}
}];
}
The Implementation of the method asyncConnectionRequst: could work as follows: take the block and hold it in an ivar. When it is appropriate call it with the correct parameter. However, having blocks as ivars or properties will increase the risk to inadvertently introduce circular references.
But, there is a better way: a wrapper block will be immediately dispatched to a suspended serial dispatch queue - which is hold as an ivar. Since the queue is suspended, they will not execute any blocks. Only until after the queue will be resumed, the block executes. You resume the queue in your connectionDidFinish: and connectionDidFailWithError: (see below):
In your NSURLConnectionDelegate:
-(void) asyncConnectionRequst:(void(^)(id result)completionHandler
{
// Setup and start the connection:
self.connection = ...
if (!self.connection) {
NSError* error = [[NSError alloc] initWithDomain:#"Me"
code:-1234
userInfo:#{NSLocalizedDescriptionKey: #"Could not create NSURLConnection"}];
completionHandler(error);
});
return;
}
dispatch_suspend(self.handlerQueue); // a serial dispatch queue, now suspended
dispatch_async(self.handlerQueue, ^{
completionHandler(self.result);
});
[self.connection start];
}
Then in the NSURLConnectionDelegate, dispatch a the handler and resume the
handler queue:
- (void) connectionDidFinishLoading:(NSURLConnection*)connection {
self.result = self.responseData;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
Likewise when an error occurred:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.result = error;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
There are even better ways, which however involve a few more basic helper classes which deal with asynchronous architectures which at the end of the day make your async code look like it were synchronous:
-(void) doFourTasksInAChainWith:(id)input
{
// This runs completely asynchronous!
self.promise = [self asyncWith:input]
.then(^(id result1){return [self auth:result1]);}, nil)
.then(^(id result2){return [self fetch:result2];}, nil)
.then(^(id result3){return [self parse:result3];}, nil)
.then(^(id result){ self.tableView.data = result; return nil;}, ^id(NSError* error){ ... })
// later eventually, self.promise.get should contain the final result
}

Ensuring the codes in different sections run in the background queue in iOS

I am new to multithreading in iOS. I need to do three things: get information from the api, parse the information and save to my database. I have these three things in a different files(getAPI,parseAPI and savetoDB). getAPI will call parseAPI and it will in return call savetoDB. I want all three of them to work in background thread.
My question is when I call getAPI, will parseAPI and savetoDB run in the background thread as well? How do I ensure that all three of them run in the background? How do I return the call back to main thread after savetoDB?
Example:
dispatch_queue_t backgroundQueue;
backgroundQueue = dispatch_queue_create("lakesh", NULL);
- (void)startprocess {
dispatch_async(backgroundQueue, ^(void) {
[self getAPI];
});
}
Need some guidance.. Thanks...
If you issue a function on a background thread, all execution will continue on that thread until it finishes or you call back another function on the main thread. I had worries like you in the beginning, so I made myself the following macros:
/// Stick this in code you want to assert if run on the main UI thread.
#define DONT_BLOCK_UI() \
NSAssert(![NSThread isMainThread], #"Don't block the UI thread please!")
/// Stick this in code you want to assert if run on a background thread.
#define BLOCK_UI() \
NSAssert([NSThread isMainThread], #"You aren't running in the UI thread!")
As you can see by the comments, I tend to use these macros at the beginning of methods I want to make sure I'm not using by error in the wrong thread. I've put these macros and more random stuff at https://github.com/gradha/ELHASO-iOS-snippets which you may find useful.
With regards to your question on returning to the main thread, since you are using GCD the best would be to call dispatch_get_main_queue() at the end of your savetoDB with the code you want to run there. If savetoDB is a library function, its entry point should allow passing in the success block you want to run on the main thread when everything finished. This is the pattern used by libraries like https://github.com/AFNetworking/AFNetworking. Note how their examples provide an API where stuff runs in the background and then your code gets called back (usually in the main thread).
Yes, parseAPI and savetoDB will run in the new queue you have created. If you need to modify the UI when the operations are finished, that code must run in the main thread. To do that, get a reference to the main queue and send it some code. For example:
- (void)startprocess {
dispatch_async(backgroundQueue, ^(void) {
[self getAPI];
dispatch_async(dispatch_get_main_queue(), ^{
// Refresh the UI with the new information
});
});
}
Don't forget to dispatch_release your new queue when you're done with it.
Another pattern, used by Cocoa itself in many parts of the framework, is to add callback block to the signatures of your API functions that is invoked when the background operation has ended. This Stack Overflow thread explains how to do that.
Yes of course if getAPI calls parseAPI, the code of parseAPI will execute on the same thread than the one getAPI was executed, so in your example on a background queue.
To return the callback to the main thread at the end, use the same techniques as Apple uses with their completionBlock you can see on multiple Apple APIs : simply pass a block (e.g. dispatch_block_t or void(^)(NSError*) or whatever fits your needs) as a parameter to your getAPI: method which will pass it to parseAPI: which will in turn pass it to savetoDB: and at the end savetoDB: can simply use dipatch_async(dispatch_get_main_queue, completionBlock); to call this block of code (passed from method to method) on the main thread.
Note: for your getAPI you can use Apple's sendAsynchronousRequest:queue:completionHandler: method, that will automatically execute the request in the background then call the completion block on the indicated NSOperationQueue (NSOperationQueue uses GCD's dispatch_queue internally). See documentation on NSOperationQueue, GCD and the Concurrency Programming Guide and all the great detailed guide in Apple doc for more info.
-(void)getAPI:( void(^)(NSError*) )completionBlock
{
NSURLRequest* req = ...
NSOperationQueue* queue = [[NSOperationQueue alloc] init]; // the completionHandler will execute on this background queue once the network request is done
[NSURLConnection sendAsynchronousRequest:req queue:queue completionHandler:^(NSURLResponse* resp, NSData* data, NSError* error)
{
if (error) {
// Error occurred, call completionBlock with error on main thread
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
} else {
[... parseAPI:data completion:completionBlock];
}
}];
}
-(void)parseAPI:(NSData*)dataToParse completion:( void(^)(NSError*) )completionBlock
{
... parse datatToParse ...
if (parsingError) {
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
} else {
[... savetoDB:dataToSave completion:completionBlock];
}
}
-(void)savetoDB:(id)dataToSave completion:( void(^)(NSError*) )completionBlock
{
... save to your DB ...
// Then call the completionBlock on main queue / main thread
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(dbError); }); // dbError may be nil if no error occurred of course, that will tell you everything worked fine
}
-(void)test
{
[... getAPI:^(NSError* err)
{
// this code will be called on the main queue (main thread)
// err will be nil if everythg went OK and vontain the error otherwise
}];
}

Resources