Is an NSInvocationOperation generated operation complete when its selector finishes? - ios

If I create an NSOperation via NSInvocationOperation, does completion of the chosen selector cause the NSOperation to complete and be removed from the operation queue?
For example:
...
NSDictionary* params = #{KEY_SERVER_ID:serverId, KEY_USERNAME:username, KEY_PASSWORD:password};
NSInvocationOperation* op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(publishBulletinBoardRead:) object:params];
[[NSOperationQueue currentQueue] addOperation:op];
...
When publishBulletinBoardRead: returns, can I assume that the operation is removed from the queue?

Related

How to get completion block of NSOperationQueue [duplicate]

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

Difference between NSOperationQueue: currentQueue/new?

I'm trying to figure out what is the difference between those 2.
Does it mean currentQueue cannot be mainQueue or it's a wrong assumption?
currentQueue return mainQueue if you call it in main-thread.
new is new queue with new threads
new is initializer method which NSOperationQueue inherits from NSObject
Documentation says:
This method is a combination of alloc and init. Like alloc, it initializes the isa instance variable of the new object so it points to the class data structure. It then invokes the init method to complete the initialization process.
This means calling +new creates new instance of NSOperationQueue
currentQueue returns you the queue on which the method was called.
NSOperationQueue docs of currentQueue says:
The operation queue that started the operation or nil if the queue could not be determined
I.e. if method calling +currentQueue is mainQueue, it can return mainQueue
prove of concept write by #Cy-4AH. Hop this can clarify a bit.
-(void) viewDidLoad {
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^(void){
//this block will executed in a separate thread (not the main thread)
if ([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]) {
NSLog(#"= in block");
} else {
NSLog(#"not = in block"); //This will be log
}
}];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:blockOperation];
//this block will executed in the main thread
if ([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]) {
NSLog(#"= outside of block"); //This will be log
} else {
NSLog(#"not = outside of block");
}
}

Cancelling an NSOperation from another NSOperation

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).

Multiple NSThreads synchronization

I want to achieve the following task using NSThread
I've a main thread and three (3) workerThread T1, T2, T3. All these started at the same time from main thread, Main thread has an int size variable. Now I want to synchronize all three threads in a way they, when my each of the above threads will execute, it will print the following:
//in main thread
- (void) mainFunction{
size = 0;
NSThread* T1 = [[NSThread alloc] initWithTarget:self
selector:#selector(workerThread:)
object:#"T1"];
[T1 start];
NSThread* T2 = [[NSThread alloc] initWithTarget:self
selector:#selector(workerThread:)
object:#"T2"];
[T2 start];
NSThread* T3 = [[NSThread alloc] initWithTarget:self
selector:#selector(workerThread:)
object:#"T3"];
[T3 start];
}
// worker thread
- (void) workerThread:(id)obj{
size++;
NSLog(#"Thread:%#--------Size:%d,obj,size)
}
I want following output:
Thread:T1-------Size:1
Thread:T2-------Size:2
Thread:T3-------Size:3
Thread:T1-------Size:4
Thread:T2-------Size:5
Thread:T3-------Size:6
Thread:T1-------Size:7
Thread:T2-------Size:8
Thread:T3-------Size:9
and return the control back to main thread when size=10
A couple of thoughts:
You say "return control back to main thread when size=10". That doesn't quite make sense. The main thread never "lost" control (as these threads are happening concurrently). Perhaps you wanted something to happen on the main thread when this situation arose?
You're not having the workerThread method do any looping, so as you've written it, each thread will do this once and then quit. You probably need to add some form of loop here.
Even if you added looping, your desired output suggests that you're assuming a particular sequence of actions that would take place, namely that these three threads will run in order (but you have no such assurances). If you needed that behavior, you'd set up a series of semaphores by which you could have one thread waiting for a signal to be sent by another.
You should be careful when updating a variable from different threads. See the Synchronization section of the Threading Programming Guide. It's simplified when dealing with a fundamental data type like your counter (just make sure you declare it as atomic). But in more substantive scenarios, you'll want to employ some synchronization technique such as #synchronized, locks, dedicated custom serial queue, etc.
In general, if you're using threads (but not necessary if using queues), you should be creating an autorelease pool for your thread.
Anyway, with these observations aside, you might have something like the following, which (a) has #autoreleasepool; (b) loops; and (c) uses a lock to make sure that the various threads synchronize their interactions with the size variable:
- (void) workerThread:(id)obj
{
#autoreleasepool {
BOOL done = NO;
while (!done) {
[self.lock lock];
if (size < 9) {
size++;
NSLog(#"Thread:%#--------Size:%d", obj, size);
}
else
{
done = YES;
}
[self.lock unlock];
// perhaps you're doing something time consuming here...
}
}
}
This assumes you have NSLock property, called lock:
#property (nonatomic, strong) NSLock *lock;
Which you created before initiating your thread test:
- (void) threadTest
{
size = 0;
self.lock = [[NSLock alloc] init];
NSThread* T1 = [[NSThread alloc] initWithTarget:self selector:#selector(workerThread:) object:#"T1"];
[T1 start];
NSThread* T2 = [[NSThread alloc] initWithTarget:self selector:#selector(workerThread:) object:#"T2"];
[T2 start];
NSThread* T3 = [[NSThread alloc] initWithTarget:self selector:#selector(workerThread:) object:#"T3"];
[T3 start];
}
Having said all this, you started this with "return control back to the main thread". As I said earlier, there's really no need to do that because in your example, your app's main thread never yielded control
For controlling relationships between tasks happening on different threads, I might suggest using GCD or operation queues. They're easier to use and have better mechanism for controlling dependencies between various tasks/operations (see Concurrency Programming Guide).
For example, consider the operation-based equivalent to your above workerThread method (identical, but no autorelease pool is needed):
- (void) operationMethod:(id)obj
{
BOOL done = NO;
while (!done) {
[self.lock lock];
if (size < 9) {
size++;
NSLog(#"Operation:%#--------Size:%d", obj, size);
}
else
{
done = YES;
}
[self.lock unlock];
// perhaps you're doing something time consuming here...
}
}
You could then create three operations (which probably will run on three threads) and wait for the result, like so:
- (void) operationTestWait
{
size = 0;
self.lock = [[NSLock alloc] init];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationMethod:) object:#"Op1"];
NSOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationMethod:) object:#"Op2"];
NSOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationMethod:) object:#"Op3"];
[queue addOperations:#[op1, op2, op3] waitUntilFinished:YES];
// do here whatever should happen when the operations are done
}
In this case, the main thread will wait for these three operations to finish.
Or, better, if these tasks take more than a few milliseconds, you should not have the main queue wait (since you should never block the main queue), but rather, you should simply define what you want the main queue to do when the three operations are done:
- (void) operationTest
{
size = 0;
self.lock = [[NSLock alloc] init];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationMethod:) object:#"Op1"];
NSOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationMethod:) object:#"Op2"];
NSOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationMethod:) object:#"Op3"];
NSOperation *completion = [NSBlockOperation blockOperationWithBlock:^{
// if you want this to do something on the main queue, then have this add it to the main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// do here whatever should happen when the operations are done
}];
}];
// define this completion operation to be dependent upon the above three operations
[completion addDependency:op1];
[completion addDependency:op2];
[completion addDependency:op3];
// now add all of them, but don't wait until finished;
// but the completion operation will only start when its dependencies
// are resolved
[queue addOperations:#[op1, op2, op3, completion] waitUntilFinished:NO];
}
Forgive the long-winded answer. If you can give us a more practical example of what these various threads will be doing and we can provide better counsel on how to best tackle it. But, in general, operation queues or dispatch queues will probably be more efficient way to tackle most concurrency related challenges.

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