NSOperation on the iPhone - iphone

I've been looking for some concrete scenarios for when NSOperation on the iPhone is an ideal tool to use in an application. To my understanding, this is a wrapper around writing your own threaded code. I haven't seen any Apple demo apps using it, and I'm wondering if I'm missing out on a great tool instead of using NSThread.
The ideal solution here would be to describe a use-case scenario for NSOperation and how you would use it to solve your problem(s).

Cocoa Is My Girlfriend has a good tutorial on the use of NSOperation and NSOperationQueue. The tutorial makes use of NSOperation to download several webpages simultaneously in separate threads.
Also, see this article from Mac Research.

The way I use it in my iPhone apps is to basically create an NSOperationQueue member in my application delegate and make it available through a property. Then every time I need to run something in the background, e.g. download some XML I'll just create an NSInvocationOperation and send it to the queque.
NSInvocationOperation *operationToPerform = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(updateXML) object:nil];
[[(MyAppDelegate *)[[UIApplication sharedApplication] delegate] sharedOperationQueue] addOperation:operationToPerform];
[op release];

In a word: NSOperationQueue
NSOperationQueue is thread safe (you can add operations to it from different threads without the need for locks) and enables you to chain NSOp objects together.
My Flickr iPhone app, Reflections, uses NSOperation and NSOperationQueue extensively to manage downloading images and XML.
Caveat: Make sure you read, re-read, and understand what the docs mean when they talk about 'concurrency'.

You should also check out this URL:
http://developer.apple.com/cocoa/managingconcurrency.html
All these above answers are great, but make sure you read the article above and make liberal use of this line in your code:
if ( self.isCancelled ) return;
That line wasn't used in the samples provided by Coca is my Girlfriend, and it wasn't until I got crash logs in from the field that I realized this was an issue/concept.

Here is just a very simple implementation but take time to read the tutorials to fully understand everything:
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(methodToCall)
object:objectToPassToMethod];
[queue addOperation:operation];

I use it for asynchronous processing. It is the best way to get data from web services or to coordinate actions that take significant time to run. Because they are thread safe, asynchronous (doesn't tie up the main thread) and they support dependencies, they are a really great tool for your toolset.
Dependencies allow you to make several separate operations and make sure the execute and succeed or error out in a certain order. This is really great when you need to synchronize a bunch of data but you need parent objects to sync before syncing child objects.

A sample that you can try using Swift
let operation : NSOperation = NSOperation()
operation.completionBlock = {
println("Completed")
}
let operationQueue = NSOperationQueue.mainQueue()
operationQueue.addOperation(operation)

Related

How to use NSOperationQueue to download audio files from server one by one

I have an array containing audio file url's. I want to fetch audio files from server using these url's in background mode. I have heard that i can achieve this with NSOperationQueue. My query is
1)How can i achieve this.
2)How can i get call back on single operation completion/failure
3)How can i get call back after completion of the whole process.
I need these call backs to keep track of downlaod process so that i can update my database about the download status of files. So, in case any internet connection loss i can download the remaning files again.
Any idea will be helpful as i am new to NSOperationQueue.
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(download:) object:aAudio];
[queue addOperation:operation];//added code
[operation release];
now do stuff what u want in method download. as per doc set [queue setMaxConcurrentOperationCount:1] for one by one.
Seems like AFNetworking has all that you need (callback blocks for success / failure, puttings requests in NSOperationQueue). In your case probably AFHttpClient and its enqueueHTTPRequestOperation method will do the job.

FMDatabase and NSOperation

I am using FMDatabase for sqlite based iphone application. The problem is that application is fetching bulk data from a web service and inserting into a local sqlite database which is blocking UI [main thread]. Also we cannot run sqlite related commands in background thread. Can we use NSOperation here ? Any example ??
You should be able to run your SQLite operations in the background, as long as you only run them inside that thread and not from the main or any other.
You could use a NSOperationQueue to handle this, setting the max number of concurrent operations to 1 to make sure only one writes to your SQLite at a time and then calling NSInvocationOperations to save your data.
NSInvocationOperation * invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(writeThisToDB) object:thisObject];
[operationQueue addOperation:invocation];

Load a UIImageView in separate thread from Disk

I need to load some images into a UIScrollView that is paging. I am using ASIHTTP so loading from the internet is already asynchronous, however I'm trying to load images from disk if i have them cached there but I don't know how to load those asynchronously (It lags between pages)
Is there any easy method that I can use without having to implement full multithreading?
Can anyone link me some good documents on how to do something like this?
Thanks in Advance.
Use...
[self performSelectorInBackground:#selector(doStuff) withObject:nil];
You can use this:
[self performSelectorInBackground:#selector(someMethod) withObject:nil];
There are other methods too for multithreading. Here's Apple's Guide on Multithreading.

Speed up loading of images

Currently I am retrieving a bunch of images from the internet and scaling them and then displaying them on my view.
The problem is I am doing it in the viewDidLoad method - so when the user taps they have to actually wait for this processing to happen and then the view is shown which causes a slight delay.
Is there anyway I could show the view and then somehow spark off the loading of the images AFTER the user has the view in front of them - similar to how a web page loads?
- (void)configureImages
{
if ([currentHotel.hotelImages count] > 0)
{
imageView1.image = [self getScaledImageFromURL: [currentHotel.hotelImages objectAtIndex:0]];
imageView2.image = [self getScaledImageFromURL: [currentHotel.hotelImages objectAtIndex:1]];
imageView3.image = [self getScaledImageFromURL: [currentHotel.hotelImages objectAtIndex:2]];
}
}
Consider NSOperation/NSOperationQueue, discussed in the Concurrency Programming Guide. There are several links to examples here.
Apps should use the asynchronous networking APIs for all networking to avoid blocking the user experience. It's best to avoid adding threads (such as happens when you use NSOperationQueue) for tasks like networking where the OS already provides async alternatives.
Apple supplies the async NSURLConnection, which works well but is a bit tedious to use. You can add a simple wrapper like gtm-http-fetcher which reduces async fetches to a single line with a callback when the load has finished. That will let you start all the loads in your viewDidLoad: method without stalling the user interface.

Stop NSThread while downloading

I have an iPhone app, where I'm displaying a tableview, that's loaded from an RSS feed. When the view is loaded, I call this method to run in a new NSThread:
- (void)start:(NSURL*)url {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSXMLParser *XMLParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
[XMLParser setDelegate:self];
if (items) {
[items release];
}
items = [[NSMutableArray alloc] init];
[self startParsing:XMLParser];
[pool drain];
}
It's working fine, but if the user leaves the view while it's downloading or parsing the xml, I want the thread to stop running, but how would I stop it from running without leaking memory? Also, if it's running the -initWithContentsOfURL: method while I want it to stop, how would I stop that method?
If you anticipate needing to control connections (i.e. stopping a connection if the user cancels or navigates away) you should probably use the asynchronous NSURLConnection API to load your data before parsing the XML. In addition to giving you the ability to close connections as needed, you'll also be able to better respond to network errors.
As NSD pointed out, you should probably implement some sort of cancel method on the class that's driving your XML parsing thread - then just use performSelector:onThread:withObject:waitUntilDone: (or similar) from your main thread when the user cancels the download or navigates away.
These are your thread stopping options
http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSThread_Class/Reference/Reference.html#//apple_ref/doc/uid/20000311-DontLinkElementID_12
And from elsewhere in the guide
"If you anticipate the need to terminate a thread in the middle of an operation, you should design your threads from the outset to respond to a cancel or exit message."
Perhaps you should look into the NSOperation and NSOperationQueue classes.
These classes give you a massive amount of control over concurrency and asynchronous execution.
The basic idea is to create a queue, and then subclass NSOperation. Inside your subclasses' main method, do the guts of your work, in this case, you could put your start method inside here.
Then you can control the operation easily, being able to set how many operations can run concurrently, set up any dependencies some operations may have on others. You can also easily cancel operations, which is what you want to do here.
Check out the documentation for NSOperation and NSOperationQueue.