Why release the NSURLConnection instance in this statement? - objective-c

I read this in a book.
-(IBAction) updateTweets
{
tweetsView.text = #"";
[tweetsData release];
tweetsData = [[NSMutableData alloc] init];
NSURL *url = [NSURL URLWithString:#"http://twitter.com/statuses/public_timeline.xml" ];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
[request release];
[activityIndicator startAnimating];
}
In this statement,is that correct to release the "connection" instance at that time? After releasing it which means this NSURLConnection instance will be destroyed since it's reference count is 0 ,how are we going to make this connection operation work after release this instance? THANKS.
I thought we should release it in one of the following callback methods,right?
connectionDidFinishLoading:
connection:didFailWithError:

It's actually fine to release it there, once the connection is sent out via initWithRequest, the only thing that matters is that the delegate exists or I believe the response will just be silently lost.
From what I can tell, the only reason to not release it there is if you want to call [connection cancel] at some point in one of the delegate functions, in which case it would be best to do what you suggest and release it in BOTH connectionDidFinishLoading and didFailWithError since only one of them will be called (right?).
Edit: For a more thorough answer, NSURLConnection initWithRequest is an asynchronous request. So it actually spawns it's own thread (but calls the delegate functions on the thread that called initWithRequest). So basically, on the thread that calls initWithRequest you are actually done with the connection object and you can release it. All the while it's doing stuff on some other thread that you don't need to be concerned with.
Also I should note that if you do release it there, make sure you DON'T release it in the finish/fail methods, because it won't be a valid object.

Related

Using data from delegate after NSURLConnection has finished

I've made an NSURLConnection, and made a seperate class to be used as a delegate, but I can't make use of the delegates data after the connection has finished. The data writes to console from within the delegate class, but not outside.
In the ServerCommunicationDelegate-class (the delegate), in method "connectionDidFinishLoading":
self.errorLog = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
NSLog(self.errorLog); // Prints the data to console
In the class where the connection takes place:
ServerCommunicationDelegate *del = [[ServerCommunicationDelegate alloc] init];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:del];
NSLog(#"Errorlog %#", del.errorLog); // Returns null
"errorLog" is a property of "ServerCommunicationDelegate".
Is there something I misunderstood about the delegation-paradigm here, or is it maybe something else I've missed?
Either way, thanks in advance.
NSURLConnection works asynchronously. The line
NSLog(#"Errorlog %#", del.errorLog); // Returns null
is executed before the connection has finished loading (probably before it even started
loading).

NSThread is failing to load Selector method

So, I am trying to connect to a remote server to get and show data. in viewDidLoad I use an NSThread to call a function called doSomething
- (void)doSomething
{
#autoreleasepool
{
NSMutableURLRequest *httpRequest = [NSMutableURLRequest requestWithURL:someURL];
[httpRequest setHTTPMethod:#"POST"];
[httpRequest setValue:[NSString stringWithFormat:#"%d", httpRequestParametersClean.length] forHTTPHeaderField:#"Content-Length"];
[httpRequest setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[httpRequest setHTTPBody:httpRequestParametersClean];
(void)[[NSURLConnection alloc] initWithRequest:httpRequest delegate:self];
for (NSString* key in response)
{
// loop through returned values
}
}
}
The code in viewDidLoad is
[NSThread detachNewThreadSelector:#selector(someURL) toTarget:self withObject:nil];
Then I have a REFRESH button which when clicked calls doSomething as well
by simply saying [self doSomething]
My problem is that when view is loaded, the response from server comes empty. I still get no response until I click on the refresh button. Strange! What am I doing wrong?
A NSURLConnection created with initWithRequest:delegate: works asynchronously, calling the delegate functions connection:didReceiveResponse:, connection:didReceiveData:, ... later, when data is read from the server. Your code does not even start the connection, so nothing will happen anyway.
The easiest way to fix your problem is to use the synchronous version
sendSynchronousRequest:returningResponse:error:
of NSURLConnection. If doSomething is executed in a separate thread, this will not block the UI.
Added: (Thanks to #geowar for mentioning this.) Note that you can also use the delegate-based NSURLConnection methods. These are more flexible (see e.g. https://stackoverflow.com/a/15591636/1187415 for a comparison). Another good choice is sendAsynchronousRequest:queue:completionHandler:, which creates a background thread automatically.

Asynchronous NSURLConnection call

I am making a Asynchronous NSURLConnection call and downloading the data, how can I know when this thread is completed?
I am making this call from viewDidLoad, and obviously NSURLConnection is intuitively running in separate thread. So how would I know if the thread is completed and second wait until the thread gets completed to get the data pulled to the viewcontroller.
You will need to assign a delegate to the NSURLConnection that handles the processing and what-not of the data. Check out the documentation, it's pretty straightforward!
code speaks for itself
-(void) startRequest {
NSURLRequest* req = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://stackoverflow.com"]];
[[NSURLConnection alloc] initWithRequest:req delegate:self];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
}

Cancel NSURLConnection Objective-C

I have a tableviewapplication, wich when the user select one view it needs to parse some XML to display information.But sometimes the XML is not finished downloading and the user can press the button to select the other view,generating a crash.I think i need to cancel the connection or something to dont cause any conflitct with the new connection,but i dont know exactly how,it suppose to be in ViewWillDisappear correct?
Heres how i start the connection on ViewDidAppear:
NSMutableURLRequest * req = [[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:#"http://Adress"]
cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20.0f];
conn = [NSURLConnection connectionWithRequest:req delegate:self];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if(conn)
{
receivedData = [[NSMutableData alloc]init];
[DSBezelActivityView newActivityViewForView:self.view withLabel:#"Loading..."];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if(conn) [conn cancel];
}
You can call NSURLConnection's cancel method and it will prevent your connections delegate from being called with any more data. You could do this in viewWillDisappear if that's when it makes sense given how your app works.
Absolutely you can cancel NSURLConnection.
But you should be careful in cancelling it too.
If you decide to cancel it in viewWillDisappear,
then
You should not do,
autorelease
and also you should not,
release it anywhere.
Here below the brief explaination:
Do cancel your NSURLConnection delegate in viewWillDisappear
[nsurlconnection cancel];
and also you should release it here not anywhere,
[nsurlconnection release];
If you release the connection in somewhere like after your xml response received then,
It will call the viewWillDisappear method anyway,
here you are cancelling it , then it will lead your app to crash.
"deallocated objects will not be cancelled"
.
And also another situation will occurs while cancelling,
If user comes and immediately navigates other view first time, your nsurlconnection will be cancelled in viewWillDisappear method.
Again the user comes to the view and escapes immediately before your nsurlconnection initialized or allocated, also your app will be crashed because,
"deallocated objects will not be cancelled".
So, check your connection != nil before you cancel it
and also don't forgot to do
nsurlconnection = nil;
in the same time.
So that you can avoid the immediate calls [nsurlconnection cancel] crashes.
SO Finally , in your viewWillDisappear method you have to do is,
Need to check nsurlconnection != nil
Need to cancel it
Need to allocate null to your nsurlconnection
Need to release it in the same method.
Sample code will be like the following,
- (void) viewWillDisappear:(BOOL)animated
{
if (nsurlconnection != nil)
{
[nsurlconnection cancel];
[nsurlconnection release];
nsurlconnection = nil;
}
}
Hope it's helpful... Happy coding ...

NSURLConnection leak?

i have set up a nsurl which grabs the data from http.
when i run instrument, it says i have a leak NSFNetwork object.
and how do i release theConnection in (void)ButtonClicked? or it will be release later on?
- (void)ButtonClicked {
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:KmlUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:20.0f];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// receivedData is declared as a method instance elsewhere
NSMutableData *receivedData = [[NSMutableData data] retain];
[self setKMLdata:receivedData];
} else {
// inform the user that the download could not be made
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// append the new data to the receivedData
// receivedData is declared as a method instance elsewhere
[KMLdata appendData:data];
NSLog(#"didReceiveData");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// release the connection, and the data object
[connection release];
[KMLdata release];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[KMLdata release];
}
I finally found the answer for this.
The error in the above code (which by the way is the near-exact sample from the SDK docs) is not in the memory management code. Autorelease is one option, manual release is another. Regardless of how you handle your NSURLConnection object, you get leaks using NSURLConnection.
First up, here is the solution. Just copy these 3 lines of code directly into connectionDidFinishLoading, didFailWithError and anywhere else you release the NSURLConnection object.
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
[sharedCache release];
Credit to mpramodjain on http://forums.macrumors.com/showthread.php?t=573253 for the code.
The problem seems to be this – the SDK caches the requests and replies on the iPhone. Even it seems if your NSMutableURLRequest cachePolicy is set to not load the reply from the cache.
The silly thing is that it seems to cache a lot of data by default. I'm transmitting a lot of data (split into multiple connections) and started to get memory warnings, and finally my App died.
The docs we need are in NSURLCache (not NSURLConnection), they state:
NSURLCache implements the caching of
responses to URL load requests by
mapping NSURLRequest objects to
NSCachedURLResponse objects. It is a
composite of an in-memory and an
on-disk cache.
Methods are provided to manipulate the
sizes of each of these caches as well
as to control the path on disk to use
for persistent storage of cache data.
Those three lines have the effect of nuking the cache totally. After adding them to my App (GPS Log), my #living object count remains steady.
Hello have you test this delegate method ?
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}
You can manage the cache more precisely.
"reset" NSURLCache *sharedCache can cause problems on other part of your code ?
This is a common question and is solved by the magic of [object autorelease]. In your code this would be as follows:
NSURLConnection *theConnection = [[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];
In this way, the object is automatically added to the "autorelease pool" and dealloc'd at the start of the next run loop after it is no longer referenced.
Hope that helps
Edit: Also, I don't see why you're needing to call -retain on your receivedData variable.
I am using the static method/autoreleased approach and it appears to work fine:
[NSURLConnection connectionWithRequest:theRequest delegate:self];
This way you don't even have to worry about releasing in the delegate callbacks. It turns out that the retain count of the connection is actually 2 (not 1) after it is alloc'd in the examples above, which changes the way I thought about this memory "leak."
#rpetrich I actually don't think you need to worry about the delegate being released before the connection is released. The connection retains it's delegate and the connection itself is actually retained by some sort of open connections queue. I wrote a blog post on my experiments with NSURLConnection on my blog:
"Potential leak of object" with NSURLConnection