views:

7461

answers:

3

I'm seeing a crash that happens 10 or 20 seconds after a POST request I make has finished (didReceiveResponse, didReceiveData and connectionDidFinishLoading all fire well before the crash happens).

This is the code I'm using to make the request:

NSURL* url = [[NSURL alloc] initWithString:urlString];
[urlString release];

NSData* requestData = [jsonData dataUsingEncoding:NSUTF8StringEncoding];
NSString* requestDataLengthString = [[NSString alloc] initWithFormat:@"%d", [requestData length]];

NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:requestData];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:requestDataLengthString forHTTPHeaderField:@"Content-Length"];
[request setTimeoutInterval:30.0];
[url release];
[requestData release];
[requestDataLengthString release];

m_URLConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 
[request release];

What's very odd about this crash is this: if I do not call setHTTPBody with my NSData object, setValue:@"application/json" for Content-Type and setValue:requestDataLengthString for Content-Length, the crash does not happen. I'm completely perplexed as to what is happening. As far as I can tell, the crash is directly related to sending an NSData object with my request. When it does crash, the top elements in the call stack for the crash (EXEC_BAD_ACCESS) are the following:

  • objc_msgSend
  • CFRelease
  • HTTPMessage::~HTTPMessage
  • _CFRelease
  • HTTPWriteFilter::~HTTPWriteFilter

Can anyone think of something I might be doing wrong? I'm completely at a loss for what I'm doing wrong, how to fix it, or how to work around it. Is there a better way to POST data than the way I'm doing?

+5  A: 

Hi,

You released an auto released object.

Remove the line [requestData release]; You don't need it. It causes the crash, since the data gets released by you, then released again when the data is done sending, which is one too many releases.

In general, you don't call release on an object unless you alloc it, or the docs explicitly say that the returned object is not autoreleased. (which is rare).

With this code, you don't need to worry that you are using an autoreleased object, as far as memory goes, no matter what you do, the memory will stay around until the underlying framework sends the data along the wire.

I don't know if there is a better way to post data - the code you have looks ok, other than the json data is likely duplicated in both a string and a data object, but the amount of data you are sending may be small. If it isn't you should expicitly release the jsonData string right after you make the data. (Which would mean that the jsonData string wuld have to be from an alloc/init call, along with the data). Or don't make the jsonData as a string, just make it as an nsmutable data right from the start, but that may be awkward.

--Tom

Tom Andersen
Damn autorelease :-) I think this is the last time I'll make this mistake, haha. Thank you!
unforgiven3
+3  A: 

You are correct in that the problem is with your NSData object. You're allocating it like so:

NSData* requestData = [jsonData dataUsingEncoding:NSUTF8StringEncoding];

According to the rules laid out in the Memory Management Programming Guide for Cocoa, you're not an owner of the data, so you shouldn't be calling release on it later on. dataUsingEncoding calls autorelease, so the object will be released the next time the autorelease pool drains. Because you're adding an extra release, the autorelease pool is going to try to release an object which has already been deallocated, which is causing the crash.

Adam Rosenfield
That was exactly it. Thank you!
unforgiven3
+1  A: 

Check the call to [urlString release]; too. if urlString was created with something such as stringWithFormat or stringwithString, you should not release it.

Roger Nolan