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.
Related
I have little tricky question :
NSOperation *operation = [[NSOperation alloc]init];
NSMutableDictionary * dictFileData = [arrData objectAtIndex:operationCounter];
[operation setCompletionBlock:^{
[self uploadFileOnAWSServer:[dictFileData valueForKey:#"MediaFileData"] MediaType:[[dictFileData valueForKey:#"MediaType"]integerValue] MediaKeyName:[dictFileData valueForKey:#"uploadKeyName"] MediaContentType:[dictFileData valueForKey:#"MediaContentType"]];
}];
[operationQueue addOperation:operation];
and for method uploadfileONAWSserve....
I have another block and its separate completion block.
In this way I have to upload multiple files on AWS server and once all files completion done I have to call service on my server.
Now I need to handle this complete scenario with multiple post with Success and failure cases.
Can anybody explain how can I handle it proper way with all success and failure cases.
Normally, you'd add dependencies to your operations:
NSOperation *uploadOperationA = // upload operation
NSOperation *uploadOperationB = // upload operation
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// do stuff when other operations complete
}];
[completionOperation addDependency:uploadOperationA];
[completionOperation addDependency:uploadOperationB];
However, you added the actual operation you want to perform in a completion block - and it is executed after the opearation's finished property is set to YES - and if an operation has dependencies, it is ready to execute when all the dependencies have finished set to YES. So in your case, the completion operation would execute before your uploads actually complete
Instead do:
NSOperation *uploadOperationA = [NSBlockOperation blockOperationWithBlock:^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self uploadFileOnAWSServer:[dictFileData valueForKey:#"MediaFileData"] MediaType:[[dictFileData valueForKey:#"MediaType"]integerValue] MediaKeyName:[dictFileData valueForKey:#"uploadKeyName"] MediaContentType:[dictFileData valueForKey:#"MediaContentType"]];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
}];
}];
dispatch_semaphore_t is necessary because, I assume, uploadFileOnAWSServer is asynchronous, and therefore will return immediately, before completing actual upload. In that case, somewhere in uploadFileOnAWSServer (perhaps completion block?) you should put
dispatch_semaphore_signal(sema);
If that function is not asynchronous, you can ditch the semaphore completely
I have a method that is being called several times. But I need to act only in the very last method call. I tried dispatch_async but didn't work because still is been queue the calls:
-(void)doingSomething:(NSString*)someValue
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:1.0f];
dispatch_async(dispatch_get_main_queue(), ^{
// do something with the last call
});
});
}
Any of you knows a way to queue the calls and only use the very last call?
I'll really appreciate your help
My suggestion would be to use a dispatch_group. Call dispatch_group_enter before you call dispatch_async, and call dispatch_group_leave and the end of the block that's executed by dispatch_async. Then, after you've enqueued all the blocks, use dispatch_group_notify to schedule the completion block, which will run after all the other dispatch_async blocks have finished.
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
...
dispatch_group_leave(group);
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// This gets called when all the other blocks have finished
});
Alternatively, you could use an NSOperationQueue instead of libdispatch, and make a completion operation which lists every other operation as a dependency. This does have the disadvantage that the completion operation won't be executed on the main queue, though.
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// This gets called when all the other operations have finished
}];
for (...) {
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
...
}];
[completionOperation addDependency:operation];
[operationQueue addOperation:operation];
}
[operationQueue addOperation:completionOperation];
This question already has answers here:
Get notification when NSOperationQueue finishes all tasks
(16 answers)
Closed 8 years ago.
How to get completion block of NSOperationQueue, here I want to spin activity indicator from start to end of all operation.
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
// Set the max number of concurrent operations (threads)
[operationQueue setMaxConcurrentOperationCount:3];
[operationQueue addOperations:#[operation, operation1, operation3,...] waitUntilFinished:NO];
Thanks.
You need to implement KVO to observe.
Go for addDependency on operation which will help you to "isFinished key" of the operation, and all dependency are resolved it performs KVN. After that you can run your logic of spin activity indicator. Also you can write a block as well. Check the following code:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operationObj = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"Show your activity...");
}];
[operationObj setCompletionBlock:^{
NSLog(#"Operation has finished...");
}];
[queue addOperation: operationObj];
Check following references URL for it
Get notification when NSOperationQueue finishes all tasks
When will completionBlock be called for dependencies in NSOperation
I have problem with NSOperations. Everything works fine but sometimes (I don't know why) Operation block is simply skipped. Am I missing something? How is it possible that operation is not even NSLogging "operation entered"? Here is some code from viewDidLoad:
//I'm using weakOperation in order to make [self.queue cancelAllOperation] method when viewWillDisappear
NSBlockOperation* operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation* weakOperation = operation;
NSString *session=#"";
#try{
session = [self getSessionId];//getting data from CoreData
}#catch(NSException *e)
{
NSLog(#"EXCEPTION WITH SESSION");
}
weakOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"operation entered");
[self downloadJSONArray]; //doing some connection downloading and using session
[self downloadImages]; //downloading images from urls from JSONs
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self refresh]; //update mainThread
}
}
[self.queue addOperation:weakOperation];
What could be scenario that coul make skip this block ?
Is there max number of threads created in iOS?
EDIT: Hey, I'have found why this happends - when a lot of applications run in the background and iOS does not have resources to queue another thread it simply skips that, how to behave in this situation?
You are assigning a new NSBlockOperation to a weak variable. Whenever you assign a new object to a weak variable, you risk having it released immediately.
If you needed a weak reference to the operation, you'd assign the object to some local variable first, and then get the weak reference for that object:
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"operation entered");
[self downloadJSONArray]; //doing some connection downloading and using session
[self downloadImages]; //downloading images from urls from JSONs
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self refresh]; //update mainThread
}
}
__weak NSBlockOperation* weakOperation = operation;
[self.queue addOperation:weakOperation];
But, as the method stands, the weakOperation is unnecessary. You generally only need weak references to avoid strong reference cycles. But no such cycle is present currently, so you can just do:
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"operation entered");
[self downloadJSONArray]; //doing some connection downloading and using session
[self downloadImages]; //downloading images from urls from JSONs
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self refresh]; //update mainThread
}
}
[self.queue addOperation:operation];
Looking at your code comment, you say "I'm using weakOperation in order to make [self.queue cancelAllOperation] method when viewWillDisappear". Using weakOperation like this will not accomplish what you want because your operation is not checking to see if it was canceled and thus it will not respond when the NSOperationQueue tries to cancel it.
If you wanted to do that, then a variation on your weakOperation pattern can be useful, but rather than using this weakOperation to add it to the queue, you can use the weak reference within the block to check to see if the operation was canceled (and you want the weak reference in the block to avoid the block from retaining the operation, itself, causing a strong reference cycle). The other key observation is that rather than creating a new NSBlockOperation, simply add an execution block to the original operation you created:
NSBlockOperation* operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation* weakOperation = operation;
[operation addExecutionBlock:^{
NSLog(#"operation entered");
if ([weakOperation isCancelled]) return;
[self downloadJSONArray]; //doing some connection downloading and using session
if ([weakOperation isCancelled]) return;
[self downloadImages]; //downloading images from urls from JSONs
if ([weakOperation isCancelled]) return;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self refresh]; //update mainThread
}];
}];
[self.queue addOperation:operation];
Clearly, if the operation is tied up in downloadJSONArray or downloadImages, it won't respond to the cancelation event until it returns from those methods. You'd have to check the cancelation status with those methods, too, if you want this operation to respond reasonably quickly to the cancellation event.
In answer to your second question, yes, there is a maximum number of threads, but it's a reasonably large number and there are other factors that come into play before the number of threads becomes an issue. The constraining factor is likely to be the downloadImages method (as you can only have 5 concurrent download requests). And even if that wasn't an issue, you'd want to constrain the number of concurrent operations, anyway, to mitigate the app's peak memory usage. If there are any network operations involved, you generally want to do something like:
self.queue.maxConcurrentOperationCount = 4; // or 5
That way, you minimize how much of the limited system resources (including threads) you are using.
By the way, I assume that downloadJSONArray and downloadImages are synchronous methods. If those are performing asynchronous network requests, you might want to consider further refactoring of the code to ensure the operation doesn't complete prematurely (e.g. wrap this in a concurrent NSOperation subclass or change those methods to run synchronously).
I've got an NSOperation queue, and four NSOperations which run in it.
NSOperationQueue myQueue = [[NSOperationQueue alloc] init];
NSOperation readOperation = [[NSOperation alloc] init];
NSOperation postOperation = [[NSOperation alloc] init];
NSOperation deleteOperation = [[NSOperation alloc] init];
I'm aware a cancel can be called an NSOperation object. If I call a
[postOperation cancel];
does it get cancelled immediately from myQueue?
Also I would like to cancel the deleteOperation from the postOperation.
Does this work?
postOperation = [NSBlockOperation blockOperationWithBlock: ^{
[deleteOperation cancel];
/**** do a HTTP post ****/
}];
[myQueue addOperation:postOperation];
Essentially I want to cancel a delete operation before I do the POST, if if that operation was executing. Also does
[myQueue setMaxConcurrentOperationCount:1];
ensure that the operation queue is FIFO?
Per NSOperation documentation:
... if an operation is in a queue but waiting on unfinished dependent operations, those operations are subsequently ignored. ... allows the operation queue to call the operation’s start method sooner and clear the object out of the queue.
the queue will call the operation's start methods immediately which should then mark it as finished without doing any useful work.
Note that you could override this method is subclasses. Apple asks you to create the same behavior as in NSOperation, but it's still up to the developer.
does [myQueue setMaxConcurrentOperationCount:1]; ensure that the operation queue is FIFO?
That's a separate question. The answer is no. You don't have control over order of operations other then setting dependencies (which is what you should be doing).