Is the Apple NSURLConnection Documentation Wrong? - iphone

// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
Since we dont' "own" receivedData by calling retain on it, aren't we leaking memory?
When are you supposed to release connection and receivedData if at all?

1/ About the connection, we use the delegate pattern to handle the memory management of this. You alloc init and set the delegate in one method. And then when the connection callback you like:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
}
or you can release the connection in any other delegate methods. This is one of reasons why they pass you back the connection. You will meet this delegate pattern a lot in iPhone like UIImagePickerController (just for another example), especially in networking problems when you have to wait until the network finish to release
2/ From the comment,
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
So, that is easy to answer, because receivedData is an instance variable, you should and can release it in dealloc method. Another choice for you is to declare a #property (nonatomic, retain) for it and then it will make sure no memory leak if you set the receivedData multiple times

This code owns both the connection and the data. The connection is created with alloc/init, requiring a release later, and the data is retained, so that too will require a release.

Related

NSURLConnection delegate methods not getting called

I am having a Server Class which has delegate methods of NSURLConnection. And I am having another class named SendRequest which will send the NSMutableURLRequest to the Server Class.
In Server Class I am having two methods called
- (void)executeAsync:(NSMutableURLRequest)urlRequest timeOutInterval:(int)timeOut
{
_urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:YES];];
}
- (void)executeSync:(NSMutableURLRequest)urlRequest
{
NSData* respData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
}
Everything works fine until here , I am able to call the above executeAsync method from SendRequest class, which is calling the delegate methods.
Now I have added a new method in the SendRequest class which will call the executeRequest method in Server class.
- (void)executeRequest:(NSMutableURLRequest)urlRequest
{
_urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:YES];];
}
This time , I got trapped . executeRequest method is getting called but it is not calling its delegate methods while processing the URL request. I am struck here for many hours.
Everything seems to be correct , I am sending the urlRequest the same way I used to send before. But I don't know why it is not working. Feel free comment , but please help me. I am really worried
EDIT :
I need to post a request to the server and get a response . If I try with synchronous it is working fine,but it is blocking the main thread. Thats the reason I am going for the asynchronous. Through delegate methods I am trying to get the response data.
I think your description of the system doesn't match the posted code, and it explains the problem you described. The SendRequest class ought to rely on the Server class to perform the server request.
It looks like the SendRequest class sets the connection delegate to self. I'm guessing all of those delegate methods are implemented on the Server class.
Try doing what your description suggested....
// SendRequest.m
- (void)executeRequest:(NSMutableURLRequest)urlRequest
{
// however you get ahold of your server...
Server *myServer = [Server sharedInstance];
// let it do the work. it has the delegate methods
[myServer executeAsync:urlRequest];
}
Alternatively, the SendRequest code can stay as it is, but set the delegate to an instance of Server class (again, assuming that's where the delegate methods are implemented). I think this second idea is worse, since the Server already knows how to start a request.
I finally solved it. Hope this will help some-one. I made code clean up and declared methods properly and finally while creating the object for the class , I have to use initwithDelegate:self . And finally NSURLConnection delegate methods are called .
set NSURLConnectionDelegate to your .h
then run the following code in .m function
NSURLConnection *connection = [[NSURLConnection alloc]
initWithRequest:request
delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[connection start];

ASIHTTPRequest Asynchronous Crash When Changing Views

I'm using ASIHttpRequest to recieve data from an xml file. However during an asynchronous request as soon as i change the view (back to the previous view using the navigation controller) the application crashes with a EXC_BAD_ACCESS on the main.m
This only happens while the request is being made.
Below is my code:
-(void)ProcessXML{
//Set url from string to url
NSURL *theurl = [NSURL URLWithString:#"http://twitter.com/statuses/user_timeline/2smssupport.xml"];
asirequest = [ASIHTTPRequest requestWithURL:theurl];
[asirequest setDelegate:self];
[asirequest startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSLog(#"Saving to Defaults");
NSData *responseData = [request responseData];
xmlSaved = responseData;
prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:responseData forKey:#"xmlDownload"];
rssParser = [[RssParser alloc] loadXMLbyURL:xmlSaved];
[self.tableView reloadData];
NSLog(#"%#",[prefs dataForKey:#"xmlDownload"]);
}
The Process XML method triggers the request and the then received data is processed in the RequestFinished.
There must be something i'm missing with the ASIHTTPRequest but i don't know what it is?
Thanks
This block of code should fix it:
-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[request clearDelegatesAndCancel]
}
If the view is being release then the delegate you set on the request is now invalid. Make sure you set the delegate to nil on the view dealloc and also stop the request.
The asirequest object isn't being retained anywhere, so it's being deallocated after ProcessXML returns.
NSZombieEnabled helps you a lot. You can tell which object is causing EXC_BAD_ACCESS.
Are you deallocating the ASIHTTPRequest object when you leave the current view? My guess is that the delegate methods are being called after your view controller has been released.
--
#Simon is right that you do need to set the delegate to nil. What I would do is:
Create an ASIHTTPRequest property in your class and set that property in your ProcessXML method. This simplifies memory management and ensures that the request object will stick around while you need it.
In both your dealloc method and your requestFinished methods, set the request delegate to nil and set self.request = nil;
At the very least, you should set the delegate to nil in your requestFinished method, but you need to remember to stop your request from running if you navigate away from this view controller before it returns, hence setting it to nil in the dealloc method as well.

How to create something like NSURLConnection?

NSURL *URL = [NSURL URLWithString:#"http://www.stackoverflow.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
With code as simple as that, I can load a webpage in my application. I don't have to worry about retaining or releasing the NSURLConnection, it will autorelease when it's done loading.
I'm creating some sort of wrapper around NSURLConnection, JSONConnection. It allows me to load a JSON value from a webpage and automatically parse that in a NSDictionary. Right now, I have to use it like this:
JSONConnection *tempJSONConnection = [[JSONConnection alloc] initWithURLString:#"http://www.stackoverflow.com" delegate:self];
self.JSONConnection = tempJSONConnection;
[tempJSONConnection release];
Then, when it's done loading, I call self.JSONConnection = nil;.
What I want, is to do this:
JSONConnection *connection = [JSONConnection connectionWithURLString:#"http://www.stackoverflow.com" delegate:self];
I know how to create this method. I just don't know how to keep connection alive when the runloop is finished and the autorelease pool is drained, and make sure connection is deallocated when it's done loading. In other words, I don't how to duplicate the exact behavior of NSURLConnection.
To all intents and purposes, from the outside, NSURLConnection effectively retains itself. This was either done by sending
[self retain];
when starting the connection and then
[self release];
when finished and after informing the delegate; or it was done by placing itself in a pool of currently open connections and removing it from that pool on completion.
You don't actually have to do any of this. NSURLConnection retains its delegate, so your JSON connection class should create an NSURLConnection passing itself as the NSURLConnection's delegate. That way it will live at least as long as the NSURLConnection. It should parse the JSON into a dictionary in the method -connectionDidFinishLoading: and pass the dictionary on to its delegate before returning. After returning the NSURLConnection will release and possibly deallocate itself and also release your JSON connection.
Someone should trac connection's live time in any case. It is a bad solutions to trac it inside the connection.
IMO the right way to do it is use singleton class to perform connections
#protocol JSONDataProviderDelegate <NSObject>
- (void) JSONProvider:(JSONDataProvider*) provider didLoadJSON:(JSONObject*) object;
- (void) JSONProvider:(JSONDataProvider*) provider didFainWithError:(NSError*) error;
#end
#interface JSONDataProvider : NSObject
+ (void) provideJSON:(NSURL*) url delegate:(id<JSONDataProviderDelegate>) delegate;
+ (void) removeDelegate:(id<JSONDataProviderDelegate>delegate);
#end
Usage:
- (void) onSomeEvent
{
[JSONDataProvider provideJSON:[NSURL URLWithString:#"http://example.com/test.json"] delegate:self];
}
- (void) JSONProvider:(JSONDataProvider*) provider didLoadJSON:(JSONObject*) object
{
NSLog(#"JSON loaded: %#", object);
}
- (void) dealloc
{
[JSONDataProvider removeDelegate:self];
[super dealloc];
}

Receiving data from URL on iPhone?

I'm using Apple Document to create an app.I succeed in connection to the server, but I receive 0 bytes from the server (no response data). I take the following steps:
I create a view-based App and add a property 'receivedData':
In ViewController.h:
#property (nonatomic, retain) NSMutableData *receivedData;
In ViewController.m:
#synthesize receivedData;
ViewController.m's action 'ViewDidLoad', I add:
receivedData = [NSMutableData alloc];
Add a button in the View and add action for it:
// create the request
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://..."]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
receivedData=[[NSMutableData data] retain];
} else {
// inform the user that the download could not be made
}
When I debugging these codes, I find that receivedData returns 0 bytes. Any ideas about what goes wrong? A simple modify of my code will be appreciated.
Your code only creates the HTTP connection - the data will only be written to and available in receivedData after the delegate callbacks have been called by the framework (once the HTTP response is received). You can get more information and sample code from Apple's documentation
The answer is the same as it was the last time I answered it for you, over at How can I receive data from URL on iPhone?. I gave a detailed explanation-- did you read it?

NSThread crashes on second call (iPhone)

I have an object and in that object I start my thread (for loading doing some URL loading).
When I have a return of my data I call a selector to perform on the main thread.
Works fine if I call it the first time, but the second time it crashes (no specific error).
[NSThread detachNewThreadSelector:#selector(doThread:)
toTarget:self
withObject:#"lala"];
-(void) doThread:(NSString *)poststring {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DataModelLocator *mydelegate = [DataModelLocator instance];
NSData *postData = [poststring dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO];
NSURL *url = [NSURL URLWithString:[mydelegate fwaservicepath]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPBody:postData];
NSURLResponse *urlResponse;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:nil];
if(data) {
[self performSelectorOnMainThread:#selector(loadDidFinishWithData:)
withObject:data
waitUntilDone:YES];
//[self loadDidFinishWithData:data];
} else {
[self performSelectorOnMainThread:#selector(loadDidFinishWithError:)
withObject:data
waitUntilDone:YES];
}
[pool release];
}
}
It crashes when I call performSelectorOnMaintThread... Could it be that it crashes on a singleton, when it got released?
When dealing with threads, avoid autoreleased objects like the plague. The autorelease pools will be drained at nondeterministic times, causing fun crashes. Use alloc/init and release on all objects involved, making sure to retain all objects that you take in on methods that are called from another thread using performSelectorOnMainThread or detachNewThreadSelector.
Garbage collection on the Mac effectively solves these problems, but the iPhone is not going to have that any time soon.
You may want to post some more information about your problem (which of the two lines crashes, what you've figured out from debugging so far, etc.) so that we can offer you some better suggestions. Without knowing which line is giving you problems, I'll hazard a guess: from the sound of it, you might have an object somewhere that's getting cleaned up by the automatic garbage collection.
Where is the variable "data" coming from? If you're creating it in the header file as a private member variable, you might have something like:
NSSomeType *data = [NSSomeType builtInInitFunction];
A variable initialized like this will normally be autoreleased, but you probably want to make sure that the garbage collection retains the instance of that object. Try something like:
// Objects initialized with init are retained
NSSomeType *data = [[NSSomeType alloc] init];
// Objects that would normally be autoreleased can be marked as retain
NSSomeType *data = [[NSSomeType builtInInitFunction] retain];
I'm not sure how your code is structured, but be sure to add at least one release for every retain and init! I'm still pretty new to Objective-C, so it's a little bit like the blind leading the blind so take my advice with a grain of salt.
Check out the "More on Memory Management" section of Learn Objective-C for more info.
EDIT2: Clarified example code. Thanks to Evan (comments) for the help.
EDIT3: I agree with Brad. Consider removing the AutoRelease pool you have an handling your alloc/init/release yourself. I don't know enough about the NSURLConnection object to know this, but is your *data memory being marked as Autorelease? If so, you may need to initialize in a different way or use retain.
Step through your code in the debugger. Figure out A) exactly which line is crashing and then B) the values of all of your variables. If you're lucky, you'll notice one is nil.