What should I do when i cancel a NSOperation? It did not remove from queue when I cancel it - iphone

As I know, a NSOperation will remove from its NSOperationQueue when I send isFinished KVO notification and isFinished=YES, and will not remove from its NSOperationQueue when I send isCancelled KVO notification. so in my cancel function:
When the operation has cancelled before, just return.
When the operation is finished, just just return.
When a NSOperation isExecuting, its queue has send isExecuting KVO notification before, so I just send isCancelled and isFinished KVO notification, then its queue will remove it.
When a NSOperation isReady, its queue has not send isExecuting KVO notification yet, so I send isExecuting KVO notification, but I got some errors in console: went isFinished=YES without being started by the queue it is in.
My question is: What should I do when I cancel an operation which state is just ready? I want it to be removed by queue.
By the way, I have a member variable to save the sate of operation, I want the cancel function do not influence its value, so When cancel an operation which state is ready, I just send isExecuting KVO notification but isExecuting=NO, because the state value is ready. Finally, my code is:

Related

Close a window without waiting for NotificationCenter post to complete Cocoa Swift

Just before calling self.window?.close() I am posting a notification with NotificationCenter.default.post(name: Notification.Name("frameCaptured"), object: nil). The Notification's selector may take some time to complete. So I want to close the window even if the selector is not complete. Is this possible or I should approach this another way?
Since the close() must be running on the main thread, all you need to do is queue a block to post the notification in the immediate future:
DispatchQueue.main.async {
NotificationCenter.default.post(name: Notification.Name("MyNotification"), object: self);
}
self.window?.close()
The notification code will execute on some future main loop. It can't start until all of the code on the current main loop has finished, but will run as soon as all pending main loop events have caught up, which in all likelihood is immediately afterward.

userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: is not fired

Able to receive the notification but delegate method is not fired after clicking on the notification.
Note :
UNUserNotificationCenter delegate received call to -userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: but the completion handler was never called.

How can I cancel an NSOperation in the same thread that the operation?

When I cancel an NSOperation (when user presses a button) cancel method is called from the main thread, but evidently the operation is running in another thread.
So, to avoid race conditions when I change _isExecuting and _isFinished, I think cancel (or at least its logic) should be called from the same thread that the NSOperation. Apart from that, when user cancels it, several files are deleted and it takes time. Because cancel is called from main thread, all the app becomes unresponsive for a while, which is ugly.
How can I execute cancel code in the same thread that the current NSOperation?
I tried this in cancel (similar to what I saw in ASIHTTPRequest):
if (_operationThread) {
[self performSelector:#selector(cancelOnRequestThread) onThread:_operationThread withObject:nil waitUntilDone:NO];
} else {
[self cancelOnRequestThread];
}
And _operationThread is setted in start method using:
_operationThread=[NSThread currentThread];
But it doesn't work.
Any idea or suggestion?
Note: I use concurrent operations, so I use start instead of main.
Thanks a lot for help.
Ricardo.
It's fine to call cancel on an NSOperation from the main thread. The cancel method is thread-safe.
That shouldn't cause any blocking on your main thread because the cancel method itself shouldn't be doing any work. If you have overridden the cancel method of your operation to delete files, etc then that is the wrong approach. You shouldn't override the cancel method, instead just check the isCancelled method at regular points within the operation's main method (e.g. inside any tight loops) and then return from main early if isCancelled returns YES, which will then cancel the operation on the same thread as the rest of the execution.
If that's how you've implemented it already and you're still having performance issues, is it possible that your operation is not really running on a background thread at all? For example if you've added it to the queue returned by [NSOperationQueue mainQueue] then that's actually running on the main application thread.

NSNotification race condition

Are there any race condition issues when using NSNotifications within a single thread? Here is a sample method:
- (void) playerToggled: (NSNotification *) notification {
if (timerPane.playing && ! timerPane.paused) {
[playerPane toggleCurrentPlayer];
[timerPane toggleTimer];
[mainPane playerToggled];
}
}
The first two calls after the condition will trigger NSNotifications that will be received by mainPane. Is mainPane guaranteed to receive the playerToggled message after those notifications? I should say that this code seems to work as desired (playerToggled always executes last). But I'm not sure what timing issues there are around notifications and I can't find a specific answer.
There are no race conditions to be expected. In addition to Dan Donaldson's answer, here is another quote from the docs for NSNotificationCenter:
A notification center delivers notifications to observers synchronously. In other words, the postNotification: methods do not return until all observers have received and processed the notification. To send notifications asynchronously use NSNotificationQueue.
I am not exactly sure what you mean, but I think this will be helpful to you:
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/Notifications/Articles/NotificationQueues.html#//apple_ref/doc/uid/20000217
Especially this part:
Using the NSNotificationCenter’s postNotification: method and its variants, you can post a notification to a notification center. However, the invocation of the method is synchronous: before the posting object can resume its thread of execution, it must wait until the notification center dispatches the notification to all observers and returns.

Proper usage of NSInvocationOperation and NSOperationQueue

I have set up an operation queue and an invocation operation. Do I need to signal that the invocation is commpleted? If not how will the operation queue knows the invocation is finished and move on to the next one? The operation queue has been set to execute one operation at a time.
No, there is no need to signal that the invocation has been completed. An NSOperationQueue knows that an operation is finished when its isFinished property is set to YES. This happens by default when the operation's -main method returns.
NSInvocationOperation's -main method, for all intents and purposes, just invokes its NSInvocation and returns, so its isFinished flag should be set to YES immediately after the invocation completes.
Boon,
It seems like what you really want here is to subclass NSOperation yourself and call your asynchronous inside of it. When the async code completes and you get your callback, you would then notify the queue via KVO that isExecuting and isFinished are updated. This is explained much more in detail over at Dave Dribin's blog:
http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/
It's automatic for NSInvocationOperation. You're already good to go.
If you need to tell other parts of your app that the operation has completed, you can use a notification. Be sure the notification goes to the right thread. On the iPhone, I send them to the main thread because I often change the UI in response to a notification, and all UI stuff must happen on the main thread.
[self performSelectorOnMainThread:#selector(postOpDoneNote) withObject:nil waitUntilDone:NO];
-(void) postOpDoneNote
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"someOpDone" object:self];
}