We have some set of actions but, each actions invokes Async APIs. We want wait till Async API got back and then start execution of second action.
Ex: We have X, Y and Z actions: Method1 does X action and method2 does Y action and Method3 does Z action. Here Method1 internally calls some Async API. So we don't want invoke Method2 until Method1 complete.
method1 ()
// Here wait till method1 complete
method2 ()
// Here wait till method12 complete
method3 ()
method 1
{
block{
// This block will be called by Async API
};
// here invoking Async API
}
What can be used to wait till method1 complete. Which mechanism of Objective-C is more efficient?
Thanks in advance
Just invoke your methods in the main thread, because Async API processes in the background thread.
You can use a dispatch_semaphore_t, which you signal at the end of your blocks (the asynchronous completion blocks). Furthermore, if method1, method2, method3 always get called in sequence then they need to share the semaphore and I would move the whole thing to as single method. Here is an example of how it can be done:
- (void) method123
{
dispatch_semaphore_t waitSema = dispatch_semaphore_create(0);
callAsynchAPIPart1WithCompletionBlock(^(void) {
// Do your completion then signal the semaphore
dispatch_semaphore_signal(waitSema);
});
// Now we wait until semaphore is signaled.
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
callAsynchAPIPart2WithCompletionBlock(^(void) {
// Do your completion then signal the semaphore
dispatch_semaphore_signal(waitSema);
});
// Now we wait until semaphore is signaled.
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
callAsynchAPIPart3WithCompletionBlock(^(void) {
// Do your completion then signal the semaphore
dispatch_semaphore_signal(waitSema);
});
// Now we wait until semaphore is signaled.
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
dispatch_release(waitSema);
}
Alternately, you can chain your calls through your completion blocks as follows:
- (void) method123
{
callAsynchAPIPart1WithCompletionBlock(^(void) {
// Do completion of method1 then call method2
callAsynchAPIPart2WithCompletionBlock(^(void) {
// Do completion of method2 then call method3
callAsynchAPIPart1WithCompletionBlock(^(void) {
// Do your completion
});
});
});
}
Related
I have implemented following completion block, one block is completed and then I update UI and object accordingly.
func doPaging() {
fetchProducts(page: pageNumber , completion: { success in
if let products = success as? Products
{
DispatchQueue.main.async {
self.products.append(contentsOf:products)
self.isWating = false;
self.productTableView.reloadData()
}
}
})
}
func fetchProducts(page: Int, completion: #escaping ((AnyObject) -> Void)) {
// URLSession call here
}
However, the following approach clearly shows restful call will happen in background thread and once it is completed, then update UI and objects.
func doPaging() {
DispatchQueue.global(qos: .background).async {
// Background Thread
fetchProducts()
DispatchQueue.main.async {
self.pageNumber += 1
self.productTableView.reloadData()
self.isWating = false
}
}
}
func fetchProducts(page: Int) {
// URLSession call here
}
I am confused between completion block method vs. DispatchQueue.
Which one is recommended?
In the first approach, you call a method fetchProducts() which internally uses NSURLSession. REST call using NSURLSession runs in background and on completion of the REST call, the completion of the task will be called. In that completion, you call your completion handler of fetchProducts(). This approach seems fine to me.
In the second approach, you use global background queue and asynchronously call NSURLSession APIs (I assume so), and don’t wait for the call to complete. The code on main queue will be instantly called and at this point the NSURLSession task may or may not have been completed.
So, this approach is problematic.
First method seems OK as long as you fetchProducts asynchronously. In fetchProducts() , if you call the completion block in the main queue you won't even need to get main queue again in the doPaging() method.
In your second method, you are calling fetchProducts() in a global (concurrent) queue. Although global queues start each task in the order they were added to queue, they run tasks concurrently. And since fechtProduct() takes time, your code block that contains self.pageNumber += 1 executed before even fetchProduct's URLSession is started. So, this approach won't work.
Completion block and Dispatch Queue are two different concepts.
Completion block is used when your function perform actions takes time to run, and need to return back and run some code even the functions has "ended". For example,
func networkCall(foo: Int, completion:#escaping (_ result:Bool)-> Void))
func otherFunc(){...}
func A(){
networkCall(foo:1){ (success) in
// handle your stuff
}
otherFunc()
}
When you run A(), it first run networkCall(), however networkCall() may takes time to run the network request and the app moved on to run otherFunc(). When the network request is done, networkCall() can call it's completion block so that A() can handle it again.
Dispatch Queue is the threading stuff safely encapsulated by Apple. Network request can be performed in Main thread as well, but it will be blocking other functions.
A common practice is to call Network request in background queue
DispatchQueue.global(qos: .background).async and call completion block after finished. If anything needs to be updated in main thread like UI, do it in the DispatchQueue.main.async
Whats the difference between
dispatch_async_f
and
dispatch_async
in ios?
The main reason behind using this async blocks is to have the background task.
dispatch_async:
By using this block you can run a code block asynchronously
Eg.
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates can be done only on main thread
});
});
dispatch_async_f:
Irrespective of the block in async task you can put your custom function to be performed in the background.
Eg:
void mainFunc(void) {} // your function
void callingFuncForAsyncTask(void*) { mainFunc(); } // new function which takes arguments for calling inside async_f
dispatch_async_f(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 0, &callingFuncForAsyncTask);
For more info and parameter details kindly refer:
https://developer.apple.com/reference/dispatch/1452834-dispatch_async_f
dispatch_async -
Submits a block for asynchronous execution on a dispatch queue and returns immediately.
This function is the fundamental mechanism for submitting blocks to a dispatch queue. Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked.
Declaration : void dispatch_async( dispatch_queue_t queue, dispatch_block_t block);
Params :
queue - the queue on which block is to be submitted & can’t be NULL.
block - block to be submitted to the target queue & can’t be NULL.
dispatch_async_f -
Submits a application defined block for async execution on a dispatch queue & returns immediately.
This function is the fundamental mechanism for submitting application-defined functions to a dispatch queue. Calls to this function always return immediately after the function has been submitted and never wait for it to be invoked.
Declaration : void dispatch_async_f( dispatch_queue_t queue, void *context, dispatch_function_t work);
Params :
queue - the queue on which block is to be submitted & can’t be NULL.
work - application defined function to be invoked on target dispatch queue 7 can’t be NULL.
I call dispatch_async(dispatch_get_main_queue(),block()) and my block() fails to perform UI interactions, because it IS not getting called in main thread, why?
Note: I use PSPDFUIKitMainThreadGuard
You have to call UI modification blocks as dispatch_sync() on the main thread, as this, it can refresh UI without being blocked.
Here is how you can do this in Swift:
runThisInMainThread { () -> Void in
// Run the method that crashes in here
}
func runThisInMainThread(block: dispatch_block_t) {
dispatch_async(dispatch_get_main_queue(), block)
}
Its included as a standard function in my repo, check it out: https://github.com/goktugyil/EZSwiftExtensions
You are not actually dispatching to the main thread, from the looks of your question, you are invoking your block and passing the return value of your block, e.g. dispatch_async(queue, yourReturnValue);
It should read:
dispatch_async(queue, yourBlock);
Not:
dispatch_async(queue, yourBlock());
this is a hard one to explain. I am creating a serial queue for handling some work in my app. Imagine that i do something like this:
dispatch_async(myQueue, { () -> Void in
self.SendSMS();
});
dispatch_async(myQueue, { () -> Void in
self.SendEmail();
});
Now what i would like to do is to only call the self.SendEmail after a delegate(SendSMS delegate) finishes its work.
Is there a simple way to do this?
Many thanks
Assuming that SendSMS is an asynchronous method, I'd advise changing SendSMS to take a completion handler closure:
// define property to hold closure
var smsCompletionHandler: (()->())?
// when you initiate the process, squirrel away the completion handler
func sendSMSWithCompletion(completion: (()->())?) {
smsCompletionHandler = completion
// initiate SMS
}
// when the SMS delegate method is called, call that completion closure
func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
// do whatever you want when done
// finally, call completion handler and then release it
smsCompletionHandler?()
smsCompletionHandler = nil
}
Thus, you'd call it like so, putting the sendEmail inside the completion closure of sendSMS:
self.sendSMSWithCompletion() {
self.sendEmail()
}
I don't know what your sendSMS and sendEmail are doing, but if you're calling the MessageUI framework, you'd generally do that on the main queue. But if you really need to do the above on your dedicated queue, then feel free to dispatch it there. But hopefully this illustrates the concept: (a) supply completion handler closure; (b) save it so your delegate can call it; and (c) when delegate is called, use that closure property and then reset it.
one way to do it, and it works is to put:
dispatch_async(myQueue, { () -> Void in
self.SendEmail();
});
at the end of the delegate. But i dont know if this is the only way to do this.
Cheers
Yes, you can do it, in next steps:
// create tasks group handle
let taskGroup = dispatch_group_create()
let mainQueue = dispatch_get_main_queue()
// write your blocks in needed order
dispatch_group_async(taskGroup, mainQueue) { [weak self] in
// execute your code
// don't forget to use self with optional, i.e.: self!.property or function
self!.SendSMS()
}
dispatch_group_async(taskGroup, mainQueue) { [weak self] in
self!.SendEmail()
}
// and of course you need to catch completion of this task group
dispatch_group_notify(taskGroup, mainQueue) {
println("All work is done, milord!")
}
UPD. The solution above is about asynchronous execution without order, as named and one of two can be completed earlier than declared order. You need to use dependencies as solution of continuously execution. I'm talking about ordering in multithreading, not about completion closures or executor pattern.
Notice, that there're more than one case to do this. One of them – below:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue) {
dispatch_sync(queue) {[weak self] in
self?.SendSMS()
}
dispatch_sync(queue) {[weak self] in
self?.SendEmail()
// here you need to call your completion of success function in main thread
}
}
Be aware, that code in your functions must exists in the same queue and use synchronous method for server requests. But this is another story ;)
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.