Stop NSThread while downloading - iphone

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.

Related

send Synchronous request using ASIHTTPREQUEST libraries showing activity indicator in iPhone

I want to show activity indicator while sending and receiving synchronous asihttprequest from server. I have used activity indicator as the asihttprequest is send but it did not showing in iPhone due to synchronous request.Any suggestion how to show activity indicator during synchronous data transfer.Thanks
Synchronous request calls your activity indicator delegate method setProgress: on the main thread.
B/c you are using ASIHTTPRequest on the main thread you are blocking the UI, hence calls to setProgress: are queuing to be dispatched after the request is finished, but by that time the progress is already 100%
To solve this use either asynchronous request or call synchronous request on a background thread using
[self performSelectorInBackground:#selector(startSynchronous:) withObject:nil];
Edit
Remember to create your own autorelease pool to handle the memory inside your startSynchronous: method
-(void)startSynchronous:(BOOL)animate{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *autoreleasedString = #"xxx";
NSLog(#"%#",autoreleasedString);
[pool drain];
}

Execute a method that takes a second to complete without temporarily halting code

Basically, i have a method that takes a few seconds to complete as it copies some files using NSFileManager. This is invoked on the touchesMoved event when the user picks up a draggable UIView icon. However, there's a slight delay before the icon's position is updated. I'm guessing it's waiting for that method to copy it's files before continuing. The method HAS to be triggered on touchesMoved, so please don't suggest moving it.
How can i execute a method that takes about a second to complete, without holding up the code?
(..and don't worry the copy method doesn't get repeatedly called from the touchesMoved event)
You could perform the task in the background using performSelectorInBackground:...:
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html
This prevent that selector from blocking the main thread.
Example:
[self performSelectorInBackground:#selector(myMethod) withObject:nil];
Do it in a background thread. Leave the main thread to deal with UI stuff only.
Technically you could divide the copying of files into very small chunks, and tell the current NSRunLoop to dispatch between each file copy.
But practically just say no to any IO access on the main thread, all IO access should be done in the background. Even the slightest block on the main thread will make the UI stutter and be unresponsive, Android user might accept that, iOS user do not.
Your options are numerous, and easy to implement. You could do a simple performSelector–:
-(void)backgroundWorker {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Do your stuff
[pool release];
}
-(void)startDoingIOStuff {
[self performSelectorInBackground:#selector(backgroundWorker)
withObject:nil];
}
You could do it practically inline using a block and GCD:
-(void)startDoingIOStuff {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL),
^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Do your stuff
[pool release];
});
}
Or you could use an NSOperation on a NSOperationQueue. I have written a longer blog post on this topic, including source code that is available here: http://blog.jayway.com/2010/08/19/future-cocoa-operation/
Before immediately resorting to a secondary thread, it would certainly be worth a try to use a plain old performSelector on self. For example:
[self peformSelector:#selector(copyFiles) withObject:nil afterDelay:0.0];
Note that this is different from doing:
[self copyFiles];
The peformSelector version basically says "do copyFiles ASAP, OK?", but doesn't block everything while waiting for it to be done. In other words, it's possible that the perform selector version would allow the main event loop to update the UI (thereby preventing the apparent visual lag) before the file copying is actually done.

iPhone UILabel not updating

I know there are lots of similar questions floating around, but none of the answers seem to fix my problem. I have an app that uses an NSURLConnection to download a file, and then does some calculations on the downloaded file. I set up a UILabel to display the current loading status (eg: "Loading file", "Parsing file"). I update the UILabel in the didReceiveResponse and connectionDidFinishLoading function of the NSURLConnection delegate, as well as some other places in my code. I update it by calling the following function:
[self performSelectorOnMainThread:#selector(updateProgress) withObject:nil waitUntilDone:NO]
where -(void)updateProgress is a function I defined to call [theLabel setNeedsDisplay]. I NSLog'd it, like
NSLog(#"theLabel: %#\n",theLabel.text);
and the information is updated correctly, but the label doesn't actually update in the view. Also, updateProgress is only called AFTER everything is loaded. It updates the label THEN, which is hardly useful. Any suggestions?
The NSURLConnection is blocking the main thread (no updates will be performed on the view until it finishes).
You can perform updateProgress in the background:
[self performSelectorInBackground:#selector(updateProgress) withObject:nil]
The first line of updateProgress should be:
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc]init];
The last lines should be:
[pool release];
pool = nil;
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html
Of course, you can also perform the NSURLConnection in the background. Then you can update the label on the main thread.

Making multiple service calls on iPhone app initialization

I need to make multiple asynchronous service calls in the application:didFinishLaunchingWithOptions: method from my application delegate in order to retrieve some data from a service to be used across various controllers in my app. I have control over the service, and I've designed the API to be as RESTful as possible, so I need to make multiple calls during app initialization.
What I want to do is to show a loading view with a progress indicator - similar to the default splash screen from Default.png - and remove that view once the service calls have completed and I have the initial values I need. This is pretty easy to do if there's only one service call, since I can simply hook that logic into the connectionDidFinishLoading: delegate method of NSURLConnection by hiding the loading view and displaying the root controller.
However, with multiple service calls, it becomes tricky. I can "chain" everything together and fire off one request, wait for it to finish/fail, then fire off the second request, and so on until I get to the last request. In the last request, I then hide the loading view and display the normal view. However, this can get unwieldy with multiple service calls, and the code becomes hard to understand and follow.
Any suggestions on the best approach for this?
I'm thinking one solution is to have a singleton class responsible for making service calls and app initialization. The singleton object will fire off all necessary requests in parallel on start, and each fail/finish callback will check if every request has finished. If all requests have finished, then it can call some method in the application delegate and tell it to hide the loading view, show the root controller, etc.
Thoughts?
Another possibility is to have each service completion callback notify (NSNotification) the controller of the progress indicator that progress has been made. You could also tell the controller of the progress indicator of how many request you were planning to make, and let it keep score, and itself do a callback when it thinks everything is done.
I am doing something similar with an NSOperationQueue that is configured to just run 1 operation at a time. See for example WeaveEngine.m and it's synchronizewithServer:credentials: method. I queue up all the separate operations, which are mostly async network calls.
you could use NSThreading and make synchronous calls in separate threads for each thing you need to get like
[NSThread detachNewThreadSelector:#selector(getDataRequest1:) toTarget:self withObject:urlRequest];
[NSThread detachNewThreadSelector:#selector(getDataRequest2:) toTarget:self withObject:urlRequest];
[NSThread detachNewThreadSelector:#selector(getDataRequest3:) toTarget:self withObject:urlRequest];
then in the selector for each thread do something like
- (void) getDataRequest1:(NSURLRequest*)urlRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSHTTPURLResponse *urlResponse;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&urlResponse error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if ([urlResponse statusCode] < 200 || [urlResponse statusCode] > 299) {
//request probably failed
}else{
[self performSelectorOnMainThread:#selector(completeRequest1:) withObject:responseData waitUntilDone:NO];
}
[pool drain];
[responseString release];
[urlRequest release];
}
of course it really depends on how many requests/threads you are wanting to spawn.
and you will need to keep track of how many you spawn vs how many finish so you can properly stop your spinner.

NSOperation on the 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)