NSBlockOperation, NSOperationQueue and Blocks

I need to synchronize a bunch of information from my RestAPI. I have to make 6 RestAPI calls to shut down. I developed API calls with blocks and returned an NSError, if any. 3 of these calls must be nested because the first call provides information to others and allows you to make, while the other 3 calls can be made independently. Due to improved network performance, I designed my synchronization request as follows:

  • 1 NSBlockOperation, which contains the first nested 3 blocks;
  • 1 NSBlockOperation, which contains the other three blocks;
  • 1 NSBlockOperation, which I use as "semphore", and tells me when everything works.

The last NSBlockOperation has a dependency on the previous two NSBlockOperation.

I also have an NSOperationQueue that contains all three NSBlockOperation, where the NSBlockOperation semaphore is added as the last in the queue. The result of what I would like to achieve: the first two blocks, called at the same time, and when their work ends, the NSBlockOperation semaphore is called and returns the controls to the user providing the UIAlertMessage.

The result is not explained earlier: controls are returned without waiting for the end of the syncAllBlocksInformation block.

Below is the code that contains NSBlockOperation:

-(void)syncAllBlocksInformation:(void(^)(NSError *error))completion{ __block NSError *blockError = nil; NSOperation *syncUserInfoOperation = [NSBlockOperation blockOperationWithBlock:^{ [dataSync syncUserInfo:tfMail.text password:tfPassword.text completion:^(NSError *error, NSNumber *idUser) { if(!error){ [dataSync syncUserfilesInfo:idUser completion:^(NSError *error) { if(!error){ [dataSync syncUserBookings:^(NSError *error) { if(error){ blockError = error; } }]; } else{ blockError = error; } }]; } else{ blockError = error; } }]; }]; NSBlockOperation *otherSyncOperations = [NSBlockOperation blockOperationWithBlock:^{ [dataSync syncNewsInfo:^(NSError *error) { if(error){ blockError = error; NSLog(@"error %@",error); } }]; }]; [otherSyncOperations addExecutionBlock:^{ [dataSync syncLocationsInfo:^(NSError *error) { if(error){ blockError = error; NSLog(@"error %@",error); } }]; }]; [otherSyncOperations addExecutionBlock:^{ [dataSync syncExoticAnimalTypesAndAnimals:^(NSError *error) { if(error){ blockError = error; NSLog(@"error %@",error); } }]; }]; NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"END"); }]; [completionOperation setCompletionBlock:^{ NSLog(@"Syc isEx %i",syncUserInfoOperation.isExecuting); NSLog(@"other isEx %i",otherSyncOperations.isExecuting); completion(blockError); }]; NSOperationQueue *opQueue = [NSOperationQueue new]; [completionOperation addDependency:syncUserInfoOperation]; [completionOperation addDependency:otherSyncOperations]; [opQueue addOperation:syncUserInfoOperation]; [opQueue addOperation:otherSyncOperations]; [opQueue addOperation:completionOperation]; } 

And here is the code that calls above the block:

 -(IBAction)login:(id)sender{ [self dismissKeyboardOpened:nil]; hud=[MBProgressHUD showHUDAddedTo:self.view animated:YES]; [hud setLabelText:NSLocalizedString(@"login_hud_message", login_hud_message )]; [hud setMode:MBProgressHUDModeIndeterminate]; [self showHudAndNetworkActivity:YES]; [self syncAllBlocksInformation:^(NSError *error) { [self showHudAndNetworkActivity:NO]; if(!error){ NSLog(@"End LOGIN"); [self showAlert:@"Login" message:@"Login OK" dismiss:YES]; } else{ [self showAlert:@"Error" message:@"Login NO" dismiss:NO]; } }]; } 

What's wrong?

+5
source share
1 answer

The problem is that NSBlockOperation is for synchronous blocks. It will be finished as soon as its block completes execution. If its block (s) disables asynchronous methods, they will be executed independently.

For example, when your syncUserInfoOperation block syncUserInfoOperation executed, it runs [dataSync syncUserInfo:...] and then considers itself complete; it does not wait for any of the completion handlers to light up, or anything like that.

A good solution for this is to create your own subclasses of NSOperation . You will probably want to create one for each of the types of data synchronization to simplify the configuration of dependencies, etc., but it is up to you. You can read all about how to do this here (be sure to read the section " Configuring operations for parallel execution ").

You can also create a general subclass of NSOperation that accepts a block that can execute asynchronously. The main problem is that it is much more difficult to handle things like canceling the operation that you probably still want.

+3
source

Source: https://habr.com/ru/post/1201432/


All Articles