OperationQueue is not removing the operations from queue if I call cancelAllOpeartions - ios

I have opeationqueue on which I am calling cancelAllOpeations but if i ask
OpearationQueue.operationcount it is not returing me zero.
I am overriding cancel method everything is working but opertioncount is not zero.is it expected?

See Apple's API Document for the NSOperation cancel method (emphasis mine):
This method does not force your operation code to stop. Instead, it updates the object’s internal flags to reflect the change in state. If the operation has already finished executing, this method has no effect. Canceling an operation that is currently in an operation queue, but not yet executing, makes it possible to remove the operation from the queue sooner than usual.
The cancel method will either mark the operation as 'ready' if it is in a queue, or mark it finished immediately if it is not in a queue. Since your operations are in a queue, this means the cancelled operations will start 'sooner'. If sub-classed correctly, your cancelled operations should immediately mark itself finished and generate its final KVO notifications. Only then will your operations be dequeued.
See also Responding to the Cancel Command for more information about cancelling custom operations.
If you need to know when the operation queue has 0 operations left in its operations array property, you might consider registering the queue's owner as an observer for the operationCount key path using KVO. Then when you are notified that the value of that property has changed, you can check to see if the value is 0 and then perform any logic required. Note that NSOperations send their KVO notifications on the thread they are operating on, which is usually going to be a background thread if they are run from an NSOperationQueue - this means that if you need to perform any UI/blocking logic, you will need to ensure it is run on the main thread.
If you decide to add an observer using KVO, make sure that you balance that by removing the observer later. In fact, if you do decide to leverage KVO, I highly suggest you digest all of the KVO programming guide and read through the KVO API docs, anything short of due-diligence when working with that framework can result in undefined behaviors, memory leaks, or even bad_access crashes.

Related

Ideal value for the URLSession - delegateQueue in Swift

We have a common Requesthandler.swift file which handles get, post http methods. We are using this file accross the App to have http request.
What should be the value for the delegateQueue, when we initialising the URLSession.
let session = URLSession(configuration: config, delegate: self, delegateQueue:OperationQueue.main)
The key consideration is that the queue should be a serial queue. As the docs say:
The queue should be a serial queue, in order to ensure the correct ordering of callbacks.
So, if you instantiate your own OperationQueue, make sure to set its maxConcurrentOperationCount to 1.
The docs go on to say:
If [the delegate queue is] nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
As such, we generally we would just leave this as nil, and let URLSession take care of this for us.
One generally would not use the main queue, though. This is largely a matter of convention (as URLSession.shared, which we generally use if we don’t need delegate methods or custom behaviors, uses a serial background queue). This practice, of using a serial background queue, is advisable, as you a lower the risk that some slow parsing operation (or whatever) in a delegate method or completion handler would ever affect your main thread responsiveness. That having been said, whenever using a serial background queue, make sure to dispatch UI updates (and the like) back to the main queue.
If you use it across the app for all http that is the correct way to do it. However, I would suggest not to create your own session. There is already something exactly for that, which is URLSession.shared, albeit with (just a few) limitations as discussed in the docs.
If you can live with the limitations great - use the shared session; otherwise use nil as Rob suggested or if you DIY the queue then you do NOT want to use main as your delegate queue. You definitely want a background queue here. If you DIY then you'd probably create a singleton queue and use that for all requests and configure it according to taste (and make it serial - see Rob's answer).
Finally, since the delegate will call the completion on the delegate queue, which is NOT main, you should switch to main in the completion whenever you need to update UI, so there will probably be a lot of switching to main there. That is not wrong nor telling you to use main as queue.

does calling cancelPreviousPerformRequestsWithTarget: on a selector cancel internal method calls as well?

I have a selector method which performs search for particular text in two different libraries in two different threads called using dispatch_async.
Now this selector is bound to a textfield and as soon some characters change we can query the libraries for the text.
Now a search takes some time say like 0.3 - 0.4 second and if the first search is not complete before another character is entered I woould like to cancel the search and re-start with new characters in the text field.
So does calling cancelPreviousPerformRequestsWithTarget on the selector cancel the internal threads and libraries call...?
No. cancelPreviousPerformRequestsWithTarget has nothing to do with blocks dispatched via GCD (i.e. dispatch_async). It cancels previous invocations of
selectors scheduled for a later time on a specific NSRunLoop using -performSelector:withObject:afterDelay:. Furthermore, it can't cancel those invocations if they're already in progress, it can only prevent them from starting, if they're still waiting to begin.
There is no means by which to (safely) forcibly cancel in flight operations, regardless of the method used to dispatch them. The operation has to, itself, support cancel-ability, usually by checking a flag periodically during its work, and returning early if the flag says the operation should cancel.
Because someone will inevitably come along and say that NSOperation supports cancelation, I might as well get it out of the way now, by pointing out that NSOperation's cancelation support still requires the operation being canceled to periodically check a flag and intentionally return early, it's just that NSOperation has the cancelled property which provides the flag for you. For that to be useful to you, your code has to know that it's executing as part of an NSOperation, and it has to have a pointer to the specific NSOperation it's executing as part of, and it still has to periodically check the cancelled property of that NSOperation and return early in order to "support cancellation."
There is no free lunch for cancellation on non-garbage-collected runtimes.

How do i cancel a background block operation in iOS?

I want to know what is the proper way to use [self.operationQueue cancelAllOperations];
I am using a block operation in async to fetch results from my API. Sometimes it happens that I get result of first request after second request and those are displayed to user.
I am using AFNetworking library for operations. Any suggestion on how I can make sure that only one request (the latest one) is active at a particular time, and previous one gets cancelled automatically.
When all operations in a queue are cancelled it is the responsibility of each running operation to stop itself. The queue will only prevent future operations from starting. With block operations there isn't really any way to stop as the block doesn't have access to the operation to check if it's cancelled.
It isn't clear exactly what you're using the operation for, but you would need to crate an operation subclass, either to run or to wrap that logic, which at least checked for cancellation before running the final callback to return the result.

Returning to a thread with GCD from a delegate

We're implementing a sync system where some operations are sent to a SAP Mobile Platform server asynchronously, and when the operations are completed, we receive a delegate call from a proprietary SDK.
This delegate method is run on a random thread, not using GCD.
We would like to do send the operations - then wait until the delegate method is called which means the operations are complete - then resume the work when this is finished (or a time-out has occured). I know this may seem synchronous, but we cannot allow the user to modify data while the operations are not finished yet to ensure data integrity.
The Sync ViewController is doing some heavy syncing work and is using GCD, and updates a progress bar and some text in the UI thread.
Now, when the delegate method is called, we want to call another method on the Sync ViewController, but this call is apparently done in the wrong thread.
My question is how to execute these methods in the same thread that GCD was executing them before the delegate was called.
Things we tried:
Just calling the methods. Then these methods are called in the wrong thread, namely the thread where the delegate method is in.
Posting a notification on a chosen thread, we don't know the exact thread where the Sync ViewController was working in.
Any ideas?
If you are already using GCD, then you must know the dispatch queue this work is being done on, so isn't it simply a case of scheduling a block, either synchronously or asynchronously, on that queue?
- (void)someDelegateMethod:(id)someValue {
dispatch_async(self.myDispatchQueue, ^{
[self doInterestingThingWith:someValue];
});
}

How to remove queued block from a GCD dispatch queue?

I am trying to re-schedule queued block that will handle the update operations.
Main goal is updating UI objects (online user table...) with minimum amount of (UI update request). (Server sometimes rain down massive amount of updates, yay!)
For simplicity main scenario is;
The dispatch_queue_t instance (queue that will handle given UI updating block) is a serial dispatch queue (private dispatch queue)
The operation (UI updating block) is scheduled with dispatch_after with t amount of time (Instead of updating for each data set update, collect update requests within t amount of time and perform a single UI update for them)
In case our data set updated, check if there already exist a scheduled event. If yes, unschedule it from dispatch_queue_t instance. Then re-schedule same block with t amount of time delay.
Also;
t is a small amount of time interval that possibly won't be noticed by the user (like 500 ms.)
Any alternative approach is welcome.
My motive behind this;
i applied same logic via Android's Handler (post & removeCallbacks combination with Runnable instance) and i hope i could achieve the same on iOS.
Edit:
As #Sven suggested usage of NSOperationQueue is more suitable for the scenario as they support cancelling each NSOperation. I skimmed through documents and found;
Canceling Operations
Once added to an operation queue, an operation object is effectively owned by the queue and cannot be removed. The only way to dequeue an operation is to cancel it. You can cancel a single individual operation object by calling its cancel method or you can cancel all of the operation objects in a queue by calling the cancelAllOperations method of the queue object.
You should cancel operations only when you are sure you no longer need them. Issuing a cancel command puts the operation object into the “canceled” state, which prevents it from ever being run. Because a canceled operation is still considered to be “finished”, objects that are dependent on it receive the appropriate KVO notifications to clear that dependency. Thus, it is more common to cancel all queued operations in response to some significant event, like the application quitting or the user specifically requesting the cancellation, rather than cancel operations selectively.
This can easily be done with GCD as well, no need to reach for the big hammer that is NSOperationQueue here.
Just use a non-repeating dispatch timer source directly instead of dispatch_after (which is just a convenience wrapper around such a timer source, it doesn't actually enqueue the block onto the queue until the timer goes off).
You can reschedule a pending timer source execution with dispatch_source_set_timer().
You cannot remove or otherwise change an operation enqueued on a dispatch queue. Try using the higher level NSOperationQueue instead which supports cancellation.

Resources