I have an NSOperation and in the completionblock I do some time consuming saving to CoreData. The actual operation runs fast. Occasionally I need to prevent this completetionblock from running. I can't see an easy way to cancel it so I tried setting a BOOl and skipping the code within it if necessary. However the operation is run within a singleton and another class needs to run the queue (that's why I need to cancel the completionblock). Any ideas on how I can cancel the completeionblock?
You can simply set completionBlock to nil:
operation.completionBlock = nil
But I think that cancelling operation's completion block from another class might be a design smell. You can end up with code which is hard to maintain and debug, as it's not clear why and when the operation's flow was changed.
An NSOperation that runs fast with a time consuming completion block seems wrong. NSOperation can be cancelled, that's its main purpose. blocks cannot be cancelled unless you do all the work itself.
I'd suggest creating a subclass of NSOperation which does all the work, including what is now done in the completion block, and the code that is now in the completion block can check that it is cancelled.
On the other hand, "saving to CoreData" doesn't seem to be something you can just cancel half way through, so the better suggestion might be to take a step back and figure out what you actually want to happen.
You can cancel the operation by calling cancel method of NSOperation.
[operation cancel];
Related
I'm calling a function on a thread in my project.
[self performSelectorInBackground:#selector(shortVibration) withObject: nil];
It's called in a loop.
I would like for the function to be called on its own thread.
I don't want it to be called at the same time (if this thread Call is in a loop... and it is)
So, I don't want to call my thread function again until the last one is done executing.
How can I do this?
don't want it to be called at the same time
That suggests a "serial queue". That could be a dispatch queue or an operation queue. But a serial queue is one that can run only one task at a time.
Or, you can decouple the loop from the repeating vibration and set up a timer to run while your loop progresses which will repeatedly call your vibration routine and then cancel the timer at the end of the loop. You can either use a standard NSTimer and have it dispatch the calls to whatever queue you want, or you can use a GCD timer, which you can schedule on a background queue.
It depends upon the details of how this vibration routine works and the nature of your loop. We'd need more detail (e.g. describe the broader problem and the nature of this "vibrate" routine) to help you further.
Perhaps you should take a look at NSOperationQueue which allows you to call functions in own created Queues. The Queues are executed on an own Thread.
For example:
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc]init];
backgroundQueue.maxConcurrentOperationCount = 1;
backgroundQueue.name = #"com.foo.bar";
[_backgroundQueue addOperationWithBlock:^{
do what you want.... here you also have access to properties in your class.
}];
With the operationCount you can handle the count of parallel executed operations. You can also create an own Subclass of NSOperation and execute your code there. Then you have to add the Operation like this [_backgroundQueue addOperation:SubclassOfNSOperation].
I hope this helps you a little. Out of your Question I can't get more information to help you more in detail. Post some code perhaps.
I need a class with a <foo> method which executes a background task.
This <foo> method is called many times from different threads: each time this method is called, i need to stop the previous allocated background threads and start a new one, so the only valid background task is the last created.
How to accomplish this?
Thanks
You can use NSOperation class so you can cancel your operation.Refer
-(void)startMyOperation
{
//Cancel the existing operation
[self cancelOperation];
self.operation = [NSBlockOperation blockOperationWithBlock:^{
//your code to be done in the background
}];
}
-(void)cancelOperation
{
if(![self.operaion isFinished])
[self.operation cancel];
}
The best way to implement cancellable operations is to use NSOperation and NSOperationQueue. Read the documentation carefully. To create an NSOperation that actually stops doing things when cancelled, the task that the NSOperation performs must actually check from time to time whether it has been cancelled and then stop doing things.
You should probably create a subclass of NSOperation, implement the "start" method, override the "isFinished" method, possibly override the "cancel" method or observe the "isCancelled" property, and when you detect that the operation is cancelled, stop doing things and change the isFinished property in a KVO compatible way (usually by sending willChangeValueForKey: and didChangeValueForKey].
The ASIHttpRequest library does that kind of thing, so it would be a good idea to look at their source code.
Many long running async methods have completion handler blocks attached as input parameters to them
I'm not sure if the completion handler should be called if the operation was cancelled.
-(void)longRunningAsyncOperation:(Input *)input completionHandler:(Block)completionHandler
{
// long running code
// periodic checks for cancelation
if(_canceled)
{
// should completion handler still be called?
return;
}
// more long running code
// completed
completionHandler(someData);
}
I don't think there's necessarily one "right answer" here. You should just make it do whatever you need to do. One option, as #Fogmeister proposed in a comment is to make the completion routine take an argument indicating whether it was canceled or completed normally. It would seem advisable to have something called in all cases so that an interested party can know that the operation was canceled.
I've seen other APIs that take two different completion blocks -- a "success" block and a "failure" block. To my mind, a single block that takes arguments to indicate status seems like a more adaptable pattern.
If you don't call any completion block on cancelation then there is effectively "lost information"; absent some other mechanism, it's impossible for the outside world to know that the operation was canceled, so it seems like one of these patterns, be it an argument to the completion, or success/failure blocks, would be preferable to simply not calling anything.
#ipmcc is correct. There is generally no one right answer, but best practices generally dictates that you should always call the completion block and, if the completion block actually cares, pass it a success/cancelled flag. The reason this is a best practice is that some memory may have been allocated as a prelude to the cancelled operation and if you don't call the completion handler, you'll never have the opportunity to free it again.
I would say yes, it shall call the completion handler.
The rationale for this is, that one can view a completion handler as some form of return value.
So, not calling the completion handler is like not returning a value in a function which is declared to return something.
I am working on an iOS app that has a highly asynchronous design. There are circumstances where a single, conceptual "operation" may queue many child blocks that will be both executed asynchronously and receive their responses (calls to remote server) asynchronously. Any one of these child blocks could finish execution in an error state. Should an error occur in any child block, any other child blocks should be cancelled, the error state should be percolated up to the parent, and the parent's error-handling block should be executed.
I am wondering what design patterns and other tips that might be recommended for working within an environment like this?
I am aware of GCD's dispatch_group_async and dispatch_group_wait capabilities. It may be a flaw in this app's design, but I have not had good luck with dispatch_group_async because the group does not seem to be "sticky" to child blocks.
Thanks in advance!
There is a WWDC video (2012) that will probably help you out. It uses a custom NSOperationQueue and places the asynchronous blocks inside NSOperationsso you can keep a handle on the blocks and cancel remaining queued blocks.
An idea would be to have the error handling of the child blocks to call a method on the main thread in the class that handles the NSOperationQueue. The class could then cancel the rest appropriately. This way the child block only need to know about their own thread and the main thread. Here is a link to the video
https://developer.apple.com/videos/wwdc/2012/
The video is called "Building Concurrent User Interfaces on iOS". The relevant part is mainly in the second half, but you'll probably want to watch the whole thing as it puts it in context nicely.
EDIT:
If possible, I'd recommend handling the response in an embedded block, which wraps it nicely together, which is what I think you're after..
//Define an NSBlockOperation, and get weak reference to it
NSBlockOperation *blockOp = [[NSBlockOperation alloc]init];
__weak NSBlockOperation *weakBlockOp = blockOp;
//Define the block and add to the NSOperationQueue, when the view controller is popped
//we can call -[NSOperationQueue cancelAllOperations] which will cancel all pending threaded ops
[blockOp addExecutionBlock: ^{
//Once a block is executing, will need to put manual checks to see if cancel flag has been set otherwise
//the operation will not be cancelled. The check is rather pointless in this example, but if the
//block contained multiple lines of long running code it would make sense to do this at safe points
if (![weakBlockOp isCancelled]) {
//substitute code in here, possibly use *synchronous* NSURLConnection to get
//what you need. This code will block the thread until the server response
//completes. Hence not executing the following block and keeping it on the
//queue.
__block NSData *temp;
response = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
[operationQueue addOperationWithBlock:^{
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
//Call selector on main thread to handle canceling
//Main thread can then use handle on NSOperationQueue
//to cancel the rest of the blocks
});
else {
//Continue executing relevant code....
}
}];
}
}];
[operationQueue addOperation:blockOp];
One pattern that I have come across since posting this question was using a semaphore to change what would be an asynchronous operation into a synchronous operation. This has been pretty useful. This blog post covers the concept in greater detail.
http://www.g8production.com/post/76942348764/wait-for-blocks-execution-using-a-dispatch-semaphore
There are many ways to achieve async behavior in cocoa.
GCD, NSOperationQueue, performSelectorAfterDelay, creating your own threads. There are appropriate times to use these mechanisms. Too long to discuss here, but something you mentioned in your post needs addressing.
Should an error occur in any child block, any other child blocks should be cancelled, the error state should be percolated up to the parent, and the parent's error-handling block should be executed.
Blocks cant throw errors up the stack. Period.
I am using NSOperation in my application.
I am cancelling the previously executing operation when create another operation. But the previously created operation's dealloc method not calling when cancelling that operation.
Pls suggest me.
Thanks.
I think what you need is that isFinished returns YES and isExecuting returns NO after cancelled. Otherwise the operation object will never be released.
Document says.
In addition to simply exiting when an operation is cancelled, it is
also important that you move a cancelled operation to the appropriate
final state. Specifically, if you manage the values for the isFinished
and isExecuting properties yourself (perhaps because you are
implementing a concurrent operation), you must update those variables
accordingly. Specifically, you must change the value returned by
isFinished to YES and the value returned by isExecuting to NO. You
must make these changes even if the operation was cancelled before it
started executing.
That's just fine:
Responding to the Cancel Command
Once you add an operation to a queue, the operation is out of your
hands. The queue takes over and handles the scheduling of that task.
However, if you decide later that you do not want to execute the
operation after all—because the user pressed a cancel button in a
progress panel or quit the application, for example—you can cancel the
operation to prevent it from consuming CPU time needlessly. You do
this by calling the cancel method of the operation object itself or by
calling the cancelAllOperations method of the NSOperationQueue class.
Canceling an operation does not immediately force it to stop what it
is doing. Although respecting the value returned by the isCancelled is
expected of all operations, your code must explicitly check the value
returned by this method and abort as needed. The default
implementation of NSOperation does include checks for cancellation.
For example, if you cancel an operation before its start method is
called, the start method exits without starting the task.
The dealloc method will be called when the retain count of the object gets to zero alas when no other object is using it.