I am writing location based app, get weather information through API by using NSURLConnection for current/other places .At first time I sent request its working successfully. But next time I want to refer the information for same place it not working while NSURLConnection is not call the any delegate methods.
this is my code:
NSString *strs=[#"http://www.earthtools.org/timezone-1.1/" stringByAppendingString:[NSString stringWithFormat:#"%#/%#",place.latitude,place.longitude]];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:strs]];
self.reqTimeZone=[NSURLConnection connectionWithRequest:request delegate:self];
[self.reqTimeZone start];
I assume you mean NSURLConnection (NSConnection doesn't exist). NSURLConnection can only be used once. See Reusing an instance of NSURLConnection.
Another gotcha with NSURLConnection is that it must be ran on a thread with a runloop. The main thread automatically has a run loop, but methods called on GCD and NSOperation threads need to have the runloop created explicitly. In practice you probably don't need to run NSURLConnection on a background thread. The download operation will not block the main thread. If you do decide to run NSURLConnection on a run loop the easiest way to do it is probably to create an NSOperation subclass and create the run loop inside of -main.
Related
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.
I am running a NSURLConnection on a separate thread (I am aware that it is asynchronous and works when running on the main thread), but it is not making delegate calls even when I pass the parent thread as the delegate. Does anyone know how to do this?
Code:
-(void)startConnectionWithUrlPath:(NSString*)URLpath {
//initiates the download connection - setup
NSURL *myURL = [[NSURL alloc] initWithString:URLpath];
myURLRequest = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[myURL release];
//initiates the download connection on a seperate thread
[NSThread detachNewThreadSelector:#selector(startDownloading:) toTarget:self withObject:self];
}
-(void)startDownloading:(id)parentThread {
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
[NSURLConnection connectionWithRequest:myURLRequest delegate:parentThread];
//The delegate methods are setup in the rest of the class but they are never getting called...
[pool drain];
}
EDIT*
The reason I need to run NSURLConnection on a separate thread is because I am downloading something in my iPhone app and the download cancels when the user locks the screen (it continues fine if the user simply presses the home button and the app goes into the background). I understand this is due to my running the connection asynchronously on the main thread and not a separate one.
I have also tried this code (NOT in a separate thread) when initiating the NSURLConnection:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:myURLRequest delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
[connection release];
But it I have the same problem with this regarding the download being cancelled on screen lock.
*UPDATE
To add to Thomas' answer below (Please note that James Webster's answer is also correct regarding the exiting of a thread) the Apple docs explain:
"Suspended state - The app is in the background but is not executing code. The system moves apps to this state automatically and does not notify them before doing so. While suspended, an app remains in memory but does not execute any code."
Since when the screen is locked by the user the app is put into the background state and than right away into the suspended state, all execution is stopped killing any downloads and no warning that this is about to happen is given... there may be a notification which tells me that the user has locked the screen but I haven't found one yet.
I therefore pause (save certain information and cancel the NSURLConnection) all downloads when the app goes into the background and resume it with the HTTP Range header when it gets active again.
This is a workaround which is ok but not ideal since the download is not occurring in the background which affects the user experience negatively... bummer.
Since your NSURLConnection is asynchronous, the end of your -startDownloading method is reached immediately, and the thread exits.
You should indeed schedule your connection on the main runloop (or use GCD).
The device lock is another issue. When the device is locked, your application is suspended to save battery life. You can probably ask for an extra amount of time when suspending in order to finish your download.
I think your problem might be that the NSURLConnection has been deallocated as soon as you exit the startDownloading: message (or more accurately when your autorelease pool is drained)
However I think your methodology might be a bit uncouth anyway. NSURLConnection the way you are using it is asynchronous and will appear to be threaded anyway.
Try this and see if it works as you expect it to (i.e. your app doesn't pause while your connection is busy)
-(void)startConnectionWithUrlPath:(NSString*)URLpath {
//initiates the download connection - setup
NSURL *myURL = [[NSURL alloc] initWithString:URLpath];
myURLRequest = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[myURL release];
[NSURLConnection connectionWithRequest:myURLRequest delegate:self];
}
I was using NSURLConnection in a synchronous way before running on a background selector, so when I moved over to ASIHTTPRequest I did the same with this framework.
So, is it a bad idea to do something like the following?
// From another method
[self performSelectorInBackground:#selector(callDatasource) withObject:nil];
- (NSData *)callDatasource {
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:someURLthatIamusing];
[request setTimeOutSeconds:50.0];
[request startSynchronous];
NSError *error = [request error];
NSData *returnedData;
if (!error) {
returnedData = [request responseData];
} else {
// do something with error
}
[self performSelectorOnMainThread:#selector(done) withObject:nil waitUntilDone:NO];
[apool release];
return returnedData;
}//end
What would be the advantage to use the ASIHTTPRequest and asynchronous methods along with the delegate methods?
From experience, sometimes odd things can happen when using ASIHTTPRequest synchronous requests off a secondary thread: the download activity icon in the status bar not disappearing upon download completion is one issue I've noticed from time to time. I've had no major problems in the past, but I use the asynchronous methods now rather than your approach. The ASI asynchronous methods are by the nature of being a widely used library more highly tested than my own implementation could ever be.
There are a number of advantages with using the asynchronous methods - you mention the delegate methods, but the latest release of ASI actually also supports blocks, which is a great leap forward (dealing with multiple synchronous calls used to be a bit of a pain due to the shared delegate methods (or unique delegates for each asynchronous call). But with blocks you can now get rid of the delegates entirely. I've found them to be really useful. Plus if you use multiple contributors it can make readability a lot easier.
Also, by doing it Async, you can more easily track progress through the setProgressDelegate command.
Before my view loads I call:
[theConnection cancel]; //assume theConnection is an NSURLConnection
I then proceed and make my proper NSURLConnection.
Will calling cancel before a connection is even made cause any problems?
No, the NSURLConnection documentation makes it clear that cancel is possible and that the delegate will receive no further messages once it is called. Also that the delegate is released on cancel.
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.