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.
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 call a network task to fetch some JSON when user selects a cell in a UICollectionView. This is asynchronous so the UI remains active whilst the data is being retrieved. Allowing user to select another cell in the UICollectionView. I do not want to stop this but I do want a way to cancel the first function call and call the new method for the now selected cell.
Is there a way to in perhaps didDeselectItemAt indexPath: to cancel any currently executing tasks?
I was thinking to place a "please wait" modal view over the UI which would prevent a second cell selection until the function returned. Is this my best option or is there a better way?
You can use a NSOperationQueue to create and keep track of the asyncronous requests and cancel them when necessary.
See the answers to this other question here:
GCD cancel async block?
By maintaining separate threads using NSOperationQueue enables controls over tasks in the middle of execution whereas GCD wont allow the same but both works for background and foreground execution of particular task mechanism.
GCD is a lightweight way to represent units of work that are going to be executed concurrently. You don’t schedule these units of work; the system takes care of scheduling for you.
NSOperation adds a little extra overhead compared to GCD, but you can add dependency among various operations and re-use, cancel or suspend them.
Sample:
var backgroundQueue = NSOperationQueue()
backgroundQueue.addOperationWithBlock(){
println("hello from background")
NSOperationQueue.mainQueue().addOperationWithBlock(){
self.theLabel.text = "updated from main thread"
}
}
Now can do various operation provided NSOperationQueue over backgroundQueue variable.
You can submit your networking tasks as NSOperations to an NSOperationQueue.
NSOperation has a cancel method, and NSOperationQueue has a cancelAllOperations method.
I am studying the NSOperation Object. According to the App Document (Listing 2-5), we can implement an asynchronously NSOperation. The part of the start part is as below:
- (void)start {
// Always check for cancellation before launching the task.
if ([self isCancelled])
{
// Must move the operation to the finished state if it is canceled.
[self willChangeValueForKey:#"isFinished"];
finished = YES;
[self didChangeValueForKey:#"isFinished"];
return;
}
// If the operation is not canceled, begin executing the task.
[self willChangeValueForKey:#"isExecuting"];
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
executing = YES;
[self didChangeValueForKey:#"isExecuting"];
}
I find out that a new thread is assigned to run the main function:
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
So, it seems that the NSOperation did nothing about the concurrent execution. The asynchrony is achieve by create a new thread. So why we need NSOperation?
You may use a concurrent NSOperation to execute an asynchronous task. We should emphasize on the fact, that the asynchronous task's main work will execute on a different thread than where its start method has been invoked.
So, why is this useful?
Wrapping an asynchronous task into a concurrent NSOperation lets you leverage NSOperationQueue and furthermore enables you to setup dependencies between other operations.
For example, enqueuing operations into a NSOperationQueue lets you define how many operations will execute in parallel. You can also easily cancel the whole queue, that is, all operations which have been enqueued.
An NSOperationQueue is also useful to associate it with certain resources - for example, one queue executes CPU bound asynchronous tasks, another executes an IO bound task, and yet another executes tasks related to Core Data, and so force. For each queue you can define the maximum number of concurrent operations.
With dependencies you can achieve composition. That means for example, you can define that Operation C only executes after Operation A and B have been finished. With composition you can solve more complex asynchronous problems.
That's it, IMHO.
I would like to mention, that using NSOperations is somewhat cumbersome, clunky and verbose, which requires a lot of boilerplate code and subclasses and such. There are much better alternatives which require third party libraries, though.
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];
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.