getting value from a NSBlockOperation? - ios

hi guys im very frustrated because i want to improve a code but i'm not getting good results this is my piece of code
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock: ^{
value1 = [self getDiferences:0.0 finx:width iniy:0.0 finy:cuartoheith image:imagen1 imagetoComapare:imagen2];
}];
[queue addOperation:blockOperation1];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock: ^{
value2 = [self getDiferences:0.0 finx:width iniy:0.0 finy:cuartoheith image:imagen1 imagetoComapare:imagen2];
}];
[queue addOperation:blockOperation2];
NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock: ^{
value3 = [self getDiferences:0.0 finx:width iniy:0.0 finy:cuartoheith image:imagen1 imagetoComapare:imagen2];
}];
[queue addOperation:blockOperation3];
NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock: ^{
value4 = [self getDiferences:0.0 finx:width iniy:0.0 finy:cuartoheith image:imagen1 imagetoComapare:imagen2];
}];
[queue addOperation:blockOperation4];
i want use this values outside the NSBlockOperation like this valuetotal=value1+value2+value3+value4; please help or help with a better solution

Add another operation which is dependent on your other operations (using addDependency:), and add your code there. Queue this operation. It will wait for all others to finish, and then use their output.
For example,
NSBlockOperation *blockOperationFinal = [NSBlockOperation blockOperationWithBlock: ^{
valueTotal = value1 + value2 + value3 + value4;
}];
[blockOperationFinal addDependency:blockOperation1];
[blockOperationFinal addDependency:blockOperation2];
[blockOperationFinal addDependency:blockOperation3];
[blockOperationFinal addDependency:blockOperation4];
[queue addOperation:blockOperationFinal];

one block operation is enough, cause it is able to add more blocks, and execute concurrently, meanwhile provide a block to trigger when all blocks are completed, like this:
NSMutableArray *array = [[NSMutableArray alloc] init];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
[array addObject:#"op1"];
}];
[operation addExecutionBlock:^{
[array addObject:#"op2"];
}];
[[[NSOperationQueue alloc] init] addOperation:operation];
[operation setCompletionBlock:^{
NSLog(#"array:%#", array);
}];

Related

Perform async task, after serial execution of other async tasks

I have a content that consists of main file and additional files.
So here is the problem: At first I need to download,unpack and insert into database additional files and only then do the same thing for main file. Additional files are needed to be downloaded serial, and main file must be downloaded after them.
What is the right way to do it?
Right now I'm doing it this way:
- (void)checkIfPlacesAreDownloaded:(NSArray *)places{
[SVProgressHUD showWithStatus:#"Downloading places"];
dispatch_group_t group = dispatch_group_create();
for(NSDictionary *place in places){
BOOL result = [IDGDatabaseManager checkIfPlaceIsDownloaded:place];
if(!result){
dispatch_group_enter(group);
[self downloadPlace:place withCompletionHandler:^{
[IDGDatabaseManager setPlaceDownloaded:[place objectForKey:#"place_ID"]
WithCompletionBlock:^(BOOL success, NSError *error) {
dispatch_group_leave(group);
}];
}];
}
}
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self downloadExcursionWithParams:self.excursionDownloadResponse];
});
}
It only works if there is one file in "places" array. If there is more than one file they start to be downloaded parallel and it is not suitable for me.
I think that downloadPlace:withCompletionHandler: method works asynchronously on the background concurrent queue. That is why the file downloads run in parallel. I'd use a private serial queue instead or simply do the next:
[SVProgressHUD showWithStatus:#"Downloading places"];
dispatch_group_t group = dispatch_group_create();
// create a serial background queue to run the file downloads
dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
dispatch_queue_t myQueue = dispatch_queue_create("com.YourApp.YourQueue", qosAttribute);
for(NSDictionary *place in places){
BOOL result = [IDGDatabaseManager checkIfPlaceIsDownloaded:place];
if(!result){
dispatch_group_enter(group);
// run the download async on the serial bg queue
__weak __typeof(self) weakSelf = self;
dispatch_async(myQueue, ^{
__typeof(self) strongSelf = self;
// we need a semaphore to wait for the download completion
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[strongSelf downloadPlace:place withCompletionHandler:^{
[IDGDatabaseManager setPlaceDownloaded:[place objectForKey:#"place_ID"]
WithCompletionBlock:^(BOOL success, NSError *error) {
dispatch_semaphore_signal(sema);
}];
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_group_leave(group);
});
}
}
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self downloadExcursionWithParams:self.excursionDownloadResponse];
});

NSOperationQueues are not executing concurrently

I am using NSOperationQueues in my application for downloading the data from the API and inserting into the local database. I am using one custom NSOpertion and adding to the NSOpertionQueue and executing for every request. When I perform two multiple requests with two different NSOperationQueues, they are not executing concurrently. I want know how to execute NSOperationQueues concurrently.
//Request for master tables data.
[RequestHandler getRequestForMasterTablesData:self];
[RequestHandler postProspectListRequest:self];
/**
* #brief ProspectList API: Gives the list of Prospects.
*/
+ (void)postProspectListRequest:(UIViewController*)lViewController {
__weak ProspectListViewController *weakSelf = (ProspectListViewController*)lViewController;
/**********************ONLINE MODE**********************/
if ([self isNetworkAvailable]) {
NSString *prospectListUrl = [NSString stringWithFormat:#"%#%#/%#",[[DataBaseManipulator sharedInstance] getCompanyURLFromUserDefaults],PROSPECTS,PROSPECTSLIST];
RequestOperationManager *op = [[RequestOperationManager alloc] initWithGetRequest:prospectListUrl];
__weak RequestOperationManager *weakOperation = op;
op.completionBlock = ^{
__strong RequestOperationManager *strong = weakOperation;
dispatch_async(dispatch_get_main_queue(), ^{
[[DataBaseManipulator sharedInstance] insertDataIntoProspectListDB:[strong.resultsDictionary valueForKey:#"prospects"]];
});
};
NSOperationQueue *networkQueue = [[NSOperationQueue alloc] init];
[networkQueue setMaxConcurrentOperationCount:1];
[networkQueue addOperation:op];
}
}
+ (void)getRequestForMasterTablesData:(UIViewController *)viewController {
if (![self isNetworkAvailable]) {
[[NetworkMonitor instance]displayNetworkMonitorAlert];
return;
}
NSString *masterTablesUrl = [NSString stringWithFormat:#"%#%#",[[DataBaseManipulator sharedInstance] getCompanyURLFromUserDefaults],MASTERTABLESDATA];
__weak LoginViewController *weakSelf = (LoginViewController*)viewController;
[[GenricUI instance] createLoadView];
RequestOperationManager *op = [[RequestOperationManager alloc] initWithGetRequest:masterTablesUrl];
__weak RequestOperationManager *weakOperation = op;
op.completionBlock = ^{
__strong RequestOperationManager *strong = weakOperation;
dispatch_async(dispatch_get_main_queue(), ^{
[[DataBaseManipulator sharedInstance] insertMasterDataIntoSalesItemTable:strong.resultsDictionary];
[[GenricUI instance] removeLoadView];
[weakSelf parseMasterTableResponse:strong.resultsDictionary];
});
};
NSOperationQueue *networkQueue = [[NSOperationQueue alloc] init];
[networkQueue setMaxConcurrentOperationCount:1];
[networkQueue addOperation:op];
}
Iam calling the above two methods at a time but they are not executing concurrently. How to execute the above two methods concurrently.
If you want concurrent execution, you shouldn't have a call
[networkQueue setMaxConcurrentOperationCount:1];
On the other hand, this code
NSOperationQueue *networkQueue = [[NSOperationQueue alloc] init];
[networkQueue setMaxConcurrentOperationCount:1];
[networkQueue addOperation:op];
is rather useless. You have a queue, add a single operation, and that's it. This doesn't achieve anything. If you want concurrent execution, create only one NSOperationQueue, keep hold of it to reuse it, and don't disable concurrent operations on it.
By the way, you seem to assume that once you have a network connection, a call to your server will succeed. That is to say the least rather optimistic.

NSBlockOperation, NSOperationQueue and Blocks

I have to sync a bunch of information from my RestAPI. I must do 6 RestAPI calls to complete work. I designed API calls with Blocks, and return NSError if there is any.
3 of these calls should to execute nested because the first call gives information to others and allows the execution while the other 3 calls can run independently.
Due to improve network performance, I designed my synchronization call as following:
1 NSBlockOperation that contains the first nested 3 blocks;
1 NSBlockOperation that contains other three blocks;
1 NSBlockOperation that I use as "semphore" and tells me when all work done.
Last NSBlockOperation has dependency to previous two NSBlockOperation.
I also have a NSOperationQueue that contains all three NSBlockOperation where the semaphore NSBlockOperation is added as last in the queue.
The result that I would to achieve is: first two blocks called Concurrently and when their work finish, the semaphore NSBlockOperation is called and returns controls to User providing UIAlertMessage.
The result isn't that previously explained: controls are returned without waiting the end of syncAllBlocksInformation block.
Below 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, code that calls above 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 ?
The issue is that NSBlockOperation is for synchronous blocks. It will be finished as soon as its block(s) have finished executing. If its block(s) fire off asynchronous methods, those will run independently.
For example, when your syncUserInfoOperation's block is executed, it fires off [dataSync syncUserInfo:...] and then considers itself done; it doesn't wait for any of the completion handlers to fire, or anything like that.
A good solution to this is to create your own NSOperation subclasses. You'd probably want to create one for each of your data sync types to make it easier to setup dependencies, etc., but that's up to you. You can read all about how to do that here (be sure to read the section on "Configuring Operations for Concurrent Execution").
You could also make a generic NSOperation subclass that takes a block that can be run asynchronously. The main issue with that is it makes it much harder to handle things like canceling the operation, which you probably still want.

Waiting on asynchronous methods using NSCondition

I am downloading four plist files asynchronously over the internet. I need to wait until all four files are downloaded, until I either on the first run, push a UIViewController, or on all subsequent runs, refresh the data, and reload all my UITableViews.
On the first run, everything works perfectly. When refreshing though, all four url requests are called, and started, but never call their completion or failure blocks, and the UI freezes. Which is odd since I preform all operations in a background thread. I have not been able to figure out why this is happening.
The first load and the refresh methods call the four "update" methods in the same way, and use NSCondition in the same way.
For the first run:
- (void)loadContentForProgram:(NSString *)programPath
{
NSLog(#"Start Load Program");
AppDelegate *myDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
hud = [[MBProgressHUD alloc] initWithView:myDelegate.window];
[myDelegate.window addSubview:hud];
hud.labelText = #"Loading...";
hud.detailsLabelText = #"Loading Data";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Do stuff here to load data from files
//Update From online files
hud.detailsLabelText = #"Updating Live Data";
resultLock = NO;
progressLock = NO;
recallLock = NO;
stageLock = NO;
condition = [[NSCondition alloc] init];
[condition lock];
[self updateCurrentCompsText];
[self updateCompetitionResults];
[self updateCompetitionRecalls];
[self updateCompetitionProgress];
while (!resultLock) {
[condition wait];
}
NSLog(#"Unlock");
while (!stageLock) {
[condition wait];
}
NSLog(#"Unlock");
while (!recallLock) {
[condition wait];
}
NSLog(#"Unlock");
while (!progressLock) {
[condition wait];
}
NSLog(#"Unlock");
[condition unlock];
updateInProgress = NO;
//Reset Refresh controls and table views
self.refreshControlsArray = [[NSMutableArray alloc] init];
self.tableViewsArray = [[NSMutableArray alloc] init];
NSLog(#"Finished Loading Program");
[[NSNotificationCenter defaultCenter] postNotificationName:#"WMSOFinishedLoadingProgramData" object:nil]; //Pushes view controller
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:myDelegate.window animated:YES];
});
});
}
When refreshing data:
- (void)updateProgramContent
{
if (!updateInProgress) {
updateInProgress = YES;
for (int i = 0; i < self.refreshControlsArray.count; i++) {
if (!((UIRefreshControl *)self.refreshControlsArray[i]).refreshing) {
[self.refreshControlsArray[i] beginRefreshing];
[self.tableViewsArray[i] setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
}
}
resultLock = NO;
stageLock = NO;
recallLock = NO;
progressLock = NO;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
condition = [[NSCondition alloc] init];
[condition lock];
[self updateCompetitionProgress];
[self updateCompetitionRecalls];
[self updateCompetitionResults];
[self updateCurrentCompsText];
while (!resultLock) {
[condition wait];
}
NSLog(#"Unlock");
while (!stageLock) {
[condition wait];
}
NSLog(#"Unlock");
while (!recallLock) {
[condition wait];
}
NSLog(#"Unlock");
while (!progressLock) {
[condition wait];
}
NSLog(#"Unlock");
[condition unlock];
});
for (int i = 0; i < self.refreshControlsArray.count; i++) {
[self.refreshControlsArray[i] performSelector:#selector(endRefreshing) withObject:nil afterDelay:1.0];
[self.tableViewsArray[i] performSelector:#selector(reloadData) withObject:nil afterDelay:1.0];
}
updateInProgress = NO;
}
}
The block below that appears in each loading method above, corresponds to a method that will download and update a specific piece of data.
[self updateCompetitionProgress];
[self updateCompetitionRecalls];
[self updateCompetitionResults];
[self updateCurrentCompsText];
which runs:
- (void)updateCompetitionResults
{
__block NSDictionary *competitionResultsData = nil;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"Some URL",[self.programName stringByReplacingOccurrencesOfString:#" " withString:#"%20"]]] cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:20.0];
AFPropertyListRequestOperation *operation = [AFPropertyListRequestOperation propertyListRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id propertyList) {
competitionResultsData = (NSDictionary *)propertyList;
[competitionResultsData writeToFile:[#"SOME LOCAL PATH"] atomically:NO];
[self updateCompetitionResultsWithDictionary:competitionResultsData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id propertyList) {
competitionResultsData = [NSDictionary dictionaryWithContentsOfFile:[#"SOME LOCAL PATH"]];
NSLog(#"Failed to retreive competition results: %#", error);
[self updateCompetitionResultsWithDictionary:competitionResultsData];
}];
[operation start];
}
and the completion and failure blocks call the same method to update the data
- (void)updateCompetitionResultsWithDictionary:(NSDictionary *)competitionResultsData
{
//Do Stuff with the data here
resultLock = YES;
[condition signal];
}
So, Why does this work on the first run, but not any of the subsequent runs?
As I mentioned in my comments, above, the most obvious problem is that you're invoking methods that use condition before you initialize condition. Make sure initialize condition before you start calling updateCompetitionResults, etc.
In terms of a more radical change, I might suggest retiring NSCondition altogether, and use operation queues:
I might use NSOperationQueue (or you can use dispatch groups, too, if you want, but I like the operation queue's ability to configure how many concurrent operations you can operate ... also if you get to a point that you want to cancel operations, I think NSOperationQueue offers some nice features there, too). You can then define each download and processing as a separate NSOperation (each of the downloads should happen synchronously, because they're running in an operation queue, you get the benefits of asynchronous operations, but you can kick off the post-processing immediately after the download is done). You then just queue them up to run asynchronously, but define a final operation which is dependent upon the other four will kick off as soon as the four downloads are done. (By the way, I use NSBlockOperation which provides block-functionality for NSOperation objects, but you can do it any way you want.)
And whereas your updateProgramContent might download asynchronously, it processes the four downloaded files sequentially, one after another. Thus, if the first download takes a while to download, it will hold up the post-processing of the others. Instead, I like to encapsulate both the downloading and the post processing of each of the four plist files in a single NSOperation, each. Thus, we enjoy maximal concurrency of not only the downloading, but the post-processing, too.
Rather than using the AFNetworking (which I'm generally a big fan of) plist-related method, I might be inclined to use NSDictionary and NSArray features that allow you to download a plist from the web and load them into the appropriate structure. These dictionaryWithContentsOfURL and arrayWithContentsOfURL run synchronously, but because we're doing this in a background operation, everything runs asynchronously like you want. This also bypasses the saving them to files. If you wanted them saved to files in your Documents directory, you can do that easily, too. Clearly, if you're doing something sophisticated in your downloading of the plist files (e.g. your server is engaging in some challenge-response authentication), you can't use the convenient NSDictionary and NSArray methods. But if you don't need all of that, the simple NSDictionary and NSArray methods, ___WithContentsOfURL make life pretty simple.
Pulling this all together, it might look like:
#interface ViewController ()
#property (nonatomic, strong) NSArray *competitions;
#property (nonatomic, strong) NSDictionary *competitionResults;
#property (nonatomic, strong) NSDictionary *competitionRecalls;
#property (nonatomic, strong) NSDictionary *competitionProgress;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self transfer];
}
- (void)allTransfersComplete
{
BOOL success;
if (self.competitions == nil)
{
success = FALSE;
NSLog(#"Unable to download competitions");
}
if (self.competitionResults == nil)
{
success = FALSE;
NSLog(#"Unable to download results");
}
if (self.competitionRecalls == nil)
{
success = FALSE;
NSLog(#"Unable to download recalls");
}
if (self.competitionProgress == nil)
{
success = FALSE;
NSLog(#"Unable to download progress");
}
if (success)
{
NSLog(#"all done successfully");
}
else
{
NSLog(#"one or more failed");
}
}
- (void)transfer
{
NSURL *baseUrl = [NSURL URLWithString:#"http://insert.your.base.url.here/competitions"];
NSURL *competitionsUrl = [baseUrl URLByAppendingPathComponent:#"competitions.plist"];
NSURL *competitionResultsUrl = [baseUrl URLByAppendingPathComponent:#"competitionresults.plist"];
NSURL *competitionRecallsUrl = [baseUrl URLByAppendingPathComponent:#"competitionrecalls.plist"];
NSURL *competitionProgressUrl = [baseUrl URLByAppendingPathComponent:#"competitionprogress.plist"];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4; // if your server doesn't like four concurrent requests, you can ratchet this back to whatever you want
// create operation that will be called when we're all done
NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// any stuff that can be done in background should be done here
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// any user interface stuff should be done here; I've just put this in a separate method so this method doesn't get too unwieldy
[self allTransfersComplete];
}];
}];
// a variable that we'll use as we create our four download/process operations
NSBlockOperation *operation;
// create competitions operation
operation = [NSBlockOperation blockOperationWithBlock:^{
// download the competitions and load it into the ivar
//
// note, if you *really* want to download this to a file, you can
// do that when the download is done
self.competitions = [NSArray arrayWithContentsOfURL:competitionsUrl];
// if you wanted to do any post-processing of the download
// you could do it here.
NSLog(#"competitions = %#", self.competitions);
}];
[completionOperation addDependency:operation];
// create results operation
operation = [NSBlockOperation blockOperationWithBlock:^{
self.competitionResults = [NSDictionary dictionaryWithContentsOfURL:competitionResultsUrl];
NSLog(#"competitionResults = %#", self.competitionResults);
}];
[completionOperation addDependency:operation];
// create recalls operation
operation = [NSBlockOperation blockOperationWithBlock:^{
self.competitionRecalls = [NSDictionary dictionaryWithContentsOfURL:competitionRecallsUrl];
NSLog(#"competitionRecalls = %#", self.competitionRecalls);
}];
[completionOperation addDependency:operation];
// create progress operation
operation = [NSBlockOperation blockOperationWithBlock:^{
self.competitionProgress = [NSDictionary dictionaryWithContentsOfURL:competitionProgressUrl];
NSLog(#"competitionProgress = %#", self.competitionProgress);
}];
[completionOperation addDependency:operation];
// queue the completion operation (which is dependent upon the other four)
[queue addOperation:completionOperation];
// now queue the four download and processing operations
[queue addOperations:completionOperation.dependencies waitUntilFinished:NO];
}
#end
Now, I don't know which of your plist's are arrays and which are dictionaries (in my example, I made competitions an array and the rest were dictionaries keyed by the competition id), but hopefully you get the idea of what I was shooting for. Maximize concurrency, eliminate NSCondition logic, really make the most of NSOperationQueue, etc.
This may be all to much to take in, but I only mention it as an alternative to NSCondition. If your current technique works, that's great. But the above outlines how I would tackle a challenge like this.

How can I extend an NSOperationQueue with dependencies for appDidEnterBackground?

I know how to extend a task for running in the background after an iOS app enters background with
beginBackgroundTaskWithExpirationHandler
dispatch_async
etc.
But what if I have an NSOperationQueue that I want to extend as background tasks, without losing the interdependencies of the NSOperations? Say I have this:
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// Do stuff
}];
NSBlockOperation *op2a = [NSBlockOperation blockOperationWithBlock:^{
// Do stuff
}];
[op2a addDependency:op1];
NSBlockOperation *op2b = [NSBlockOperation blockOperationWithBlock:^{
// Do stuff
}];
[op2b addDependency:op1];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
// Do stuff
}];
[op3 addDependency:op2a];
[op3 addDependency:op2b];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations: #[op1, op2a, op2b, op3] ];
Is there an elegant way to have the NSOperationQueue finish in the background?
I realized that I didn't fully understand how background thread extension works.
After calling beginBackgroundTaskWithExpirationHandler to start the background extension, I can do whatever I want in the background. I thought there is just one thread extended, but it's in fact the whole application that keeps running.
Therefore I just have to call endBackgroundTask at the end of the last NSOperation to achieve what I want.

Resources