I'm trying with the following code get metadata of Dropbox dir, calling loadMetadata Dropbox API and hope the callback (loadedMetadata) is called when data are in device. This is the code:
_semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
NSLog(#"loadMetadata() in async block");
[self.restClient loadMetadata:#"/"];
});
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
As you can see, I'm calling in async mode, to get the metadata in one property and then return in other method. I'm make the following call in loadedMetadata callback:
dispatch_semaphore_signal(_semaphore);
Good, the issue here is that loadedMetadata callback is never called. I don't know why, but the trace I put in loadedMetadata is never printed and application is freeze (waiting loadedMetadata signal). I put DISPTACH_TIME_FOREVER as example, please not what you have in mind.
From https://www.dropbox.com/developers/core/start/ios:
Make sure you call DBRestClient methods from the main thread or a
thread that has a run loop. Otherwise the delegate methods won't be
called.
You should call loadMetadata from the main thread. The actual operation is async already.
Related
Here is a sample code -
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray* arr1 = #[newPost.postid];
[[LoginService defaultService].client
invokeAPI:#"GetPhoneNumbers"
body:arr1
HTTPMethod:#"POST"
parameters:nil
headers:nil
completion:^(id result, NSHTTPURLResponse* response, NSError* error)
{
.......
The dispatch_async is called from the main thread. Within the async code there is a call made to a backend which has a completion block
In this code the completion block never gets called, no breakpoints are hit, no nslog gets printed.
I first thought that maybe because the thread that ran the async code is dying off without waiting for the completion handler, and I tried with a infinite while loop to keep the thread alive and check, but that was not the case. Even then the completion handler is not called.
I tried to find examples which did something like this - call a method with a completion handler from within an async call, but couldn't find much material.
What am I missing here ?
NSURL Async Request gets freeze until the previous api call to complete.
Our project has a requirement to upload a video into the server, we are doing this using GCD. But, UI should not be freeze until the upload going to complete. So, there is a possible to call some other apis too. But, while calling other apis, these new api calls are waiting for the completion of video uploading api call to finish in queue.
Flow of the application is as follows:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//api call for video upload is an NSMutableUrlRequest with Asynchronous completion handler.
dispatch_async(dispatch_get_main_queue(), ^{
//UI operations
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//simple api call for getting minimal data/ validation --- (Problem - This api is getting freeze until the video upload to complete).
});
});
});
If you don't want that innermost dispatch to wait for the completion of the video, move it outside of that dispatch block, e.g.:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//api call for video upload is an NSMutableUrlRequest with Asynchronous completion handler.
dispatch_async(dispatch_get_main_queue(), ^{
//UI operations
});
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//simple api call for getting minimal data/ validation
});
I must say, the original construct seems to suggest some implied dependency between the video and that data validation calls, but if not, you can use something like the above. If you need to know when these two separate asynchronous tasks are both done, you could use a dispatch group.
Did you take a look at AFNetworking? It uses NSURLConnection / NSURLSession for making requests and they are all asynchronous.
I am new to IOS development and am currently facing a problem.
When method A is called, it calls method B and then it wait for delegate connectionDidFinish which connectionDidFinish will execute MethodC.
My question is how do I ensure that methodA to methodC has finished executing before executing NSLog?
I found that a way to solve this problem is to use notification center. Send notification to me after finishing executing methodC. I don't think this is a good solution. Is there another way to do this?
Example:
[a methodA];
NSLog(#"FINISH");
If any of those methods perform actions asynchronously, you can't. You'll have to look into a different way of doing this. I personally try to use completion blocks when ever I can, although it's perfectly fine to do this other ways, like with delegate methods. Here's a basic example using a completion block.
- (void)someMethod
{
[self methodAWithCompletion:^(BOOL success) {
// check if thing worked.
}];
}
- (void)methodAWithCompletion:(void (^) (BOOL success))completion
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, kNilOptions), ^{
// go do something asynchronous...
dispatch_async(dispatch_get_main_queue(), ^{
completion(ifThingWorked)
});
});
}
In the code you posted, methodA must finish executing before the log statement will execute.
However, if methodA starts an asynchronous process that takes a while to finish and returns before it is finished, then you need to do something different. Usually you don't want to freeze the user interface while you are waiting, so you set up a delegate, pass in a completion block, or wait for an "ok, I'm done" notification.
All those are very valid, good ways to solve the problem of waiting for asynchronous tasks to finish running.
Newer APIs are starting to use completion blocks. Examples are:
presentViewController:animated:completion:, which takes a completion
block that gets called once the new view controller is fully
on-screen and "ready for business.
animateWithDuration:animations:completion:, which takes a completion
block that gets executed once the animation is finished, and
sendAsynchronousRequest:queue:completionHandler:, which starts an
asynchronous URL request (usually an HTTP GET or PUT request) and
provides a completion block that gets called once the request has
been completed (or fails)
I am parsing an XML web service and after that parse finishes I want to call another method. But my code calls the method during the parse process. What I want is to wait till the parse process end. Here is my code:
ArsivNoCheck *arsivNoCheck = [ArsivNoCheck alloc];
[arsivNoCheck checkArsivNo:_txtArsivNo.text]; //Here I call parsing method in another class
//Here I call the method
[self performSelectorOnMainThread:#selector(sampleMethod) withObject:nil waitUntilDone:YES];
-(void) sampleMethod
{
//some code
}
You should consider NSOperation, and its method completionBlock.
Then, you would be able to perform your parsing, and at the end of it, execute some code.
Note : If you plan to update the UI, take care, because the completionBlock is not necessarily running on the main thread!
From NSOperation's Doc reference :
completionBlock
Returns the block to execute when the operation’s main
task is complete.
-(void (^)(void))completionBlock
Return Value
The block to execute after the operation’s main task is completed. This block takes no
parameters and has no return value.
Discussion
The completion block you provide is executed when the value
returned by the isFinished method changes to YES. Thus, this block is
executed by the operation object after the operation’s primary task is
finished or cancelled.
Example :
[filterOp setCompletionBlock: ^{
NSLog(#"Finished filtering an image.");
}];
See this tutorial on Ray Wenderlich's site for implementation.
I'm using below code to download some data from the web. Am I right that I need to retain the data like I have done? Also the NSLog statement from inside the block shows that the array has been populated, but when I run the NSLog outside the block the arrays show as (null). How would I save the data outside dispatch_async method?
__block NSArray *downloadedCareerIds;
__block NSArray *diskCareerIds;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* Download stuff */
downloadedCareerIds = [[CareersParser idsFrom:#"web"] retain];
diskCareerIds = [[CareersParser idsFrom:#"disk"] retain];
DLog(#"downloadedCareerIds: %#", downloadedCareerIds);
DLog(#"diskCareerIds: %#", diskCareerIds);
});
DLog(#"downloadedCareerIds: %#", downloadedCareerIds);
DLog(#"diskCareerIds: %#", diskCareerIds);
The idea of dispatch_async is that you give it a block of code to execute asynchronously, therefore giving up any control of when that code gets executed. The call to dispatch_async returns once the block has been enqueued, NOT once the block has finished executing (hence async). Therefore, the log statements inside of the block you're passing to dispatch_async will get executed, almost always, after the log statements below your call to dispatch_async.
dispatch_async is a non blocking method so it will return immediately. So when the DLog statements outside the block are called, they will mostly not have been set. Hence you don't see the values you get from the internal log statements.
If you want to act on the data within the same method, you will have to either send a blocking dispatch_sync which is pointless or you can call the methods within the block.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
....
[self doStuffWithTheArrays];
});
Once the block is executed the objects will be available provided they are instance variables or you will lose the references.