I am looking for a small scenario that how can we trace the "dispatch_async" is running or not?.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
//back ground process
});
In my case, my app will be in foreground I started the back ground thread and when I bring app from background to foreground I need to check whether it is still running or not. I should not call the same process if it is still running. any idea?
The easiest way to do this (without keeping a reference to every dispatch or a flag for entering/leaving asynchronous tasks) is by using dispatch_group notifications. See the example link and code below:
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
// 1
__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create();
for (NSInteger i = 0; i < 3; i++) {
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
dispatch_group_enter(downloadGroup); // 2
Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
dispatch_group_leave(downloadGroup); // 3
}];
[[PhotoManager sharedManager] addPhoto:photo];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4
if (completionBlock) {
completionBlock(error);
}
});
}
Note how:
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4
if (completionBlock) {
completionBlock(error);
}
});
will not be called until after
dispatch_group_leave(downloadGroup); // 3
is called.
You should setup your threading to where you can work with callbacks like this to determine states. You should try to avoid using boolean flags at all costs, as this is exactly what dispatch groups are for. It's also hard to keep track of numerous asynchronous calls using boolean states.
link: dispatch groups
The question is wrong - dispatch_async is running while you call it and stops running when the call returns, which is practically immediately. What you really want to know is whether the dispatched block is running or not. The simplest way is something along the lines of
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self blockIsRunning:YES];
// do stuff
[self blockIsRunning:NO];
});
or if you want to know whether the block has run once, you would do something like
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self blockStarted];
// do stuff
[self blockFinished];
});
Alternatively, use NSOperationQueue and a subclass of NSOperation so instead of an anonymous block you have a proper object that you can ask whether it is ready, cancelled, executing, or finished.
Related
In my iOS application I am using Core Data.
For table View listing I use NSFetchedResultsController and
Connecting to Remote store I use NSIncrementalStore.
My FetchedResultsController Context is having MainQueue Cuncurrency type.(I couldn't do it with a PrivateQueueCurrencyTYpe).
For resolving Fault, for a many relationship, the executeFetchResultsCall:withContext:error method is executed from my IncrementalStore subclass.
Inside the executeFetchResults method, I will invoke the API (connecting to remote server) if it is not available in my local database.
myarray = [object representationsForRelationship:#"manyconnection" withParams:nil];
Now I need the results array in return synchronously to be returned to the ExecuteFetchResultsMethod. Also this operation should be executed on Main thread.
So I am having only one option to fetch the results from server which causes the UI to unresponsive for the specified sleep time.
-(RequestResult*)makeSyncJsonRequest{
__block RequestResult *retResult = [[RequestResult alloc] init];
__block BOOL block = YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
void (^resultBlock)(RequestResult*) = ^(RequestResult* result){
if(!retResult.error)
retResult = result;
block = NO;
dispatch_group_leave(group);
};
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
dispatch_group_enter(group);
[self makeAsyncJsonRequestWithBlock:resultBlock];
});
// Do some other work while the tasks execute.
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return retResult;
}
As the above operation is being executed on main thread,the UI hangs.
Inorder to make the UI smoother, I need to carry out the executeFetchrequest in some other thread which is not possible.
It also expects the results array in return.
Is there any option to carry out this something like in a completion handler manner?
or
Any alternate methods or design to work this proper.
Any Help is highly appreciated.
This is a skeleton, using a dispatch_group, assuming you are using an NSFetchedResultsController to update your UITableView:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// do your setup (FetchedResultsController and such)
[self syncData];
}
- (void)syncData
{
NSArray<Entity*> *results = [self fetchData];
BOOL needsUpdateFromServer = YES; // check your results and set this bool accordingly
if (!needsUpdateFromServer) {
return;
}
__block ServerResponse *fromServer = nil;
__block dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self loadDataFromServer:^(ServerResponse *response) {
fromServer = response;
dispatch_group_leave(group);
}];
dispatch_group_notify(group,dispatch_get_main_queue(),^{
[self persistData:fromServer];
/*
According to our discussion, you are using an NSFetchedResultsController.
So your tableView should update automatically after persisting the data.
*/
});
}
- (void)loadDataFromServer:(void (^)(ServerResponse *response))completion
{
// [someDownloadService downloadDataFromServerInBackgroundWithCompletion:^(ServerResponse* response){
dispatch_async(dispatch_get_main_queue(), ^{
completion(response);
});
// }];
}
- (NSArray<Entity*>*)fetchData
{
NSArray<Entity*> *results = nil;
// fetch from core data and return it
return results;
}
- (void)persistData:(NSArray<ServerResponse*> *)serverResponses
{
// parse whatever you get from server
// ... and persist it using Core Data
}
#end
I need to create an entity on the server and then upload few images to the server.
So first block display success creating entity on the server then I starting upload 10 images one by one in cycle, but the app send the notification not after last 10 image was uploaded, so 'i' variable can be 10 even not be a 10 in the order. I am not sure but seems iteration in the block is not right. So I just want to be sure that the 10 images was uploaded and just then invoke sending notification.
So I skip some blocks parameters and failure options, array that I use for getting images to upload and etc. Just think about my blocks as an example that display success invocation after '{'.
// success first block
block1
{
// cycle from 0 to 10
for (NSInteger i = 0; i <=10; i++)
{
// success upload image to the server block
block2
{
// if I did 10 uploads I need to send notification.
if (i == 10)
{
// send notification here when last block returned success....
}
}
}
}
If you're creating a bunch of AFHTTPRequestOperation objects, then the most logical approach would be to create a completion operation and make it dependent upon the request operations:
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// code to be run when the operations are all done should go here
}];
for (...) {
// create `operation` AFHTTPRequestOperation however you want and add it to some queue
// but just make sure to designate the completion operation dependency
[completionOperation addDependency:operation];
}
// now that all the other operations have been queued, you can now add the completion operation to whatever queue you want
[[NSOperationQueue mainQueue] addOperation:completionOperation];
You can use Dispatch Group as the following.
// success first block
block1
{
dispatch_group_t group = dispatch_group_create();
// cycle from 0 to 10
__block NSUInteger successCount = 0;
for (NSInteger i = 0; i <=10; i++)
{
dispatch_group_enter(group);
// upload image to the server block
successOrErrorBlock
{
if (success)
successCount++;
dispatch_group_leave(group);
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (successCount == 10) {
// send notification here when last block returned success....
}
});
}
The pattern I follow when invoking more than a couple blocks asynchronously is to create a to-do list (array) and perform that list recursively, like this:
// say this is our asynch operation. presume that someParameter fully
// describes the operation, like a file to be uploaded
- (void)performSomeAsynchOperationDefinedBy:(id)someParameter completion:(void (^)(BOOL, NSError *))completion {
// this could wrap any operation, like anything from AFNetworking
}
- (void)doOperationsWithParameters:(NSArray *)parameters completion:(void (^)(BOOL, NSError *))completion {
if (!parameters.count) return completion(YES, nil);
id nextParameter = someParameters[0];
NSArray *remainingParameters = [parameters subarrayWithRange:NSMakeRange(1, parameters.count-1)];
[self performSomeAsynchOperationDefinedBy:nextParameter completion:^(BOOL success, NSError *error)) {
if (!error) {
[self doManyOperations:remainingParameters completion:completion];
} else {
completion(NO, error);
}
}];
}
Now, to do several operations, place the parameters in an array, like:
NSArray *parameters = #[#"filename0", #"filename1", ...];
[self doOperationsWithParameters:parameters completion:(BOOL success, NSError *error) {
NSLog(#"ta-da!");
}];
A variant on the above with a progress block invoked up front and after each recursion (with a progress percentage initialCount/remainingCount) should be straight forward from the example provided.
Another variation would be to wrap both the param array and the completion block in a single NSObject, so the recursive call could be made with performSelector:, the benefit of doing this would be to not wind up the stack on a long recursion.
I have a for loop containing three asynchronous methods, and I want to make some treatment after this 3 async methods are finished.
-(void)getAllUsersInformations{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for(User *user in users){
[self getUserInfo:user];
}
//Here, I want to reload the table view for example, after finishing the for loop (executing the whole three methods).
});
}
-(void)getUserInfo:(User*)user{
[self getInformations:user];
[self getExperiences:user];
[self getEducation:user];
}
Do you have any technic to have this result?
Thank you very much.
One GCD approach is to use dispatch_group. So, before you start an asynchronous task, call dispatch_group_enter, and then when the asynchronous task finishes, call dispatch_group_leave, and you can then create a dispatch_group_notify which will be called when the asynchronous tasks finish. You can marry this with a completion block pattern (which is a good idea for asynchronous methods, anyway):
If getInformations, getExperiences and getEducation are, themselves, all asynchronous methods, the first thing you need is some mechanism to know when they're done. A common solution is to implement a completion block pattern for each. For example:
// added completionHandler parameter which will be called when the retrieval
// of the "informations" is done.
- (void)getInformations:(User*)user completionHandler:(void (^)(void))completionHandler {
// do whatever you were before, but in the asynchronous task's completion block, call this
// completionHandler()
//
// for example
NSURLRequest *request;
[NSURLConnection sendAsynchronousRequest:request queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// handle the request here
// the important thing is that the completion handler should
// be called _inside_ the this block
if (completionHandler) {
completionHandler();
}
}];
}
Repeat this process for getExperiences and getEducation, too.
Then, you can use a dispatch group to notify you of when each of these three requests are done done, calling a completion block in getUserInfo when that takes place:
// added completion handler that will be called only when `getInformations`,
// `getExperiences` and `getEducation` are all done.
//
// this takes advantage of the completion block we added to those three
// methods above
- (void)getUserInfo:(User*)user completionHandler:(void (^)(void))completionHandler {
dispatch_group_t group = dispatch_group_create();
// start the three requests
dispatch_group_enter(group);
[self getInformations:user completionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self getExperiences:user completionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self getEducation:user completionHandler:^{
dispatch_group_leave(group);
}];
// this block will be called asynchronously only when the above three are done
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (completionHandler) {
completionHandler();
}
});
}
And you then repeat this process at the getAllUsersInformations:
// call new getUserInfo, using dispatch group to keep track of whether
// all the requests are done
-(void)getAllUsersInformations {
dispatch_group_t group = dispatch_group_create();
for(User *user in users){
dispatch_group_enter(group);
[self getUserInfo:user completionHandler:^{
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
Two final thoughts:
Having outlined all of that, I must confess that I would probably wrap these requests in concurrent/asynchronous custom NSOperation subclasses instead of using dispatch groups. See the "Configuring Operations for Concurrent Execution" section of the Concurrency Programming Guide. This is a more radical refactoring of the code, so I won't tackle that here, but it lets you constrain the number of these requests that will run concurrently, mitigating potential timeout issues.
I don't know how many of these user requests are going on, but you might want to consider updating the UI as user information comes in, rather than waiting for everything to finish. This is, again, a more radical refactoring of the code, but might lead to something that feels more responsive.
Try to do a block with completion, you can't do this with a for loop if the methods are async. you have to call getUserInfo one by one after the completion of the previous. I think this gonna be solved your problem.
-(void)getAllUsersInformations{
[self registerUserAtIndex:0];
}
- (void) registerUserAtIndex: (NSInteger ) userIndex
{
RegisterOperation *op = [[RegisterOperation alloc] initWithUser:[users objectAtIndex:userIndex]];
[RegisterOperation setResultCompletionBlock:^(BOOL *finished, NSInteger userIndex) {
dispatch_async(dispatch_get_main_queue(), ^{
if (userIndex++ < [users count] {
[self registerUserAtIndex:userIndex++];
} else {
[myTableView reloadData];
}
}];
[[NSOperationQueue mainQueue] addOperation:op];
}
Hope this will help you.
Rop Answer with swift:
func processData()
{
let group: dispatch_group_t = dispatch_group_create()
for item in data as! Object {
dispatch_group_enter(group)
item.process(completion: {() -> (Void) in
dispatch_group_leave(group)
})
}
dispatch_group_notify(group, dispatch_get_main_queue(), {
//Do whatever you want
})
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
for(User *user in users){
[self getUserInfo:user];
}
dispatch_async(dispatch_get_main_queue(), ^{
//reload tableview , this is on main thread.
});
});
I follow RAY WENDERLICH GCD tutorial- part 2, and I don't get this:
First implementation
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create(); // 2
for (NSInteger i = 0; i < 3; i++) {
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
dispatch_group_enter(downloadGroup); // 3
Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
dispatch_group_leave(downloadGroup); // 4
}];
[[PhotoManager sharedManager] addPhoto:photo];
}
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5
dispatch_async(dispatch_get_main_queue(), ^{ // 6
if (completionBlock) { // 7
completionBlock(error);
}
});
});
}
Second Implementation:
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
// 1
__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create();
for (NSInteger i = 0; i < 3; i++) {
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
dispatch_group_enter(downloadGroup); // 2
Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
dispatch_group_leave(downloadGroup); // 3
}];
[[PhotoManager sharedManager] addPhoto:photo];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4
if (completionBlock) {
completionBlock(error);
}
});
}
I the first implementation the relevant code is surrounded with dispatch_async and everything is super clear.
BUT, the second implementation is unclear! I don't get it, how the GCD mechanism is taking any part beside of the notification for enter and leave?
The first implementation runs a background thread starting at
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
So the whole function runs there. But this background thread gets eventually blocked at this line (it waits for all the photos to be downloaded):
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER);
This is all fine since you are not blocking the main thread. But it is a bit of a clumsy implementation since,
you block a thread where you really have not to and
the code looks a bit ugly with the dispatch_async around the whole function.
So the second implementation has two benefits:
It's not that "ugly" and a bit more readable (because you get rid of the dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{})
and there is no block, because dispatch_group_notify serves as the asynchronous completion block.
The first method is an asynchronous method. You can call it from any thread, including the main thread. It will perform actions in a background thread, and when it's done, it will dispatch the callback block on the main thread. A call to this method returns immediately, the callback will be called a lot later.
The second method is a blocking method. The method will not finish until the photos are downloaded, and then it calls your callback method. This method should most definitely not be called from the main thread, only from a background thread.
I'm trying to do a share operation where I call a function with async block but in my next if statement I need to get the value which is completed in the block to continue. This is my code which will highlight more detail. I heard about NSLock and tried using it but it didnt work, may be I'm doing something lock, I'm not much familiar with locks.
-(void) shareOperation
{
__block NSString *resultText;
BOOL continueSharing;
NSLock *conditionLock=[[NSLock alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[conditionLock lock];
[self performSomeAsynchronousOperation completionBlock:^(NSError *tError, bool status)
{
dispatch_async(dispatch_get_main_queue(),
^{
if (status)
{
resultText = #"Operation completed. Would you like to continue?";
}
else
{
resultText = #" Operation failed. Would you still like to continue?";
}
UIAlertView *result = [UIAlertView alertViewWithTitle:nil message:resultText cancelButtonTitle:#"Cancel" otherButtonTitles:[NSArray arrayWithObjects:#"OK",nil] onDismiss:^(int buttonIndex)
{
NSLog(#"selected button index: %d",buttonIndex);
if (buttonIndex == 0)
{
continueSharing=YES;
[conditionLock unlock];
NSLog(#"We are continuing sharing :)");
}
}onCancel:^{
continueSharing=NO;
[conditionLock unlock];
NSLog(#"cancelled");
}]; [result show];
});
}];
});
}
//should continue only after earlier if block finishes, ie after getting the continueSharing value
if (continueSharing)
{
[self performSomeAnotherAsynchronousOperation];
}
}
Rather than using locks (which are only designed to ensure that there is not simultaneous access to some shared resource), you could use semaphores (which are designed so that one thread can wait for a signal from another thread, which is needed here).
So, you should create a semaphore:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
The alert view completion blocks would signal that semaphore:
dispatch_semaphore_signal(semaphore);
And where you want to wait for that signal (before performSomeAnotherAsynchronousOperation):
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
I tweaked your code a bit, but most notably, changed it so that it would not block the main queue (which you never want to do) by making sure the dispatch_semaphore_wait is done in a background queue. Also note that the dispatch_semaphore_signal is not inside the if statement. This resulted in:
- (void)shareOperation
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__block BOOL continueSharing = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self performSomeAsynchronousOperationWithCompletionBlock:^(NSError *tError, bool status){
dispatch_async(dispatch_get_main_queue(),^{
NSString *resultText;
if (status)
resultText = #"Operation completed. Would you like to continue?";
else
resultText = #"Operation failed. Would you still like to continue?";
UIAlertView *alertView = [UIAlertView alertViewWithTitle:nil message:resultText cancelButtonTitle:#"Cancel" otherButtonTitles:#[#"OK"] onDismiss:^(int buttonIndex) {
NSLog(#"selected button index: %d",buttonIndex);
if (buttonIndex == 0) {
continueSharing = YES;
NSLog(#"We are continuing sharing :)");
}
dispatch_semaphore_signal(semaphore);
} onCancel:^{
dispatch_semaphore_signal(semaphore);
NSLog(#"cancelled");
}];
[alertView show];
});
}];
// should continue only after earlier if block finishes, ie after getting the continueSharing value
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (continueSharing)
[self performSomeAnotherAsynchronousOperation];
});
}
Even better, you should not use any blocking mechanism like semaphores (certainly not on the main queue), but rather one should simply have the completion block for the alert view initiate the next step of the process directly, itself. I understand that you say that this is not practical in your scenario, but it is generally the correct way to handle these scenarios.
You can use the delay block
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC);
dispatch_after(delayTime, dispatch_get_main_queue(), ^(void){
})