I'm currently trying to write an iPhone companion to a website that I'm developing. I'm trying to get JSON data from my website by making a controller a delegate of the NSURLConnection. Here's the problem though, I have an NSMutableData object named responseData initialized like so:
responseData = [NSMutableData dataWithLength:0];
And I want to append data as it comes:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
However, this causes my app to crash and says in the console:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[__NSCFArray appendData:]: unrecognized selector sent to instance
0x6d0a640'
I'm pretty confident that appendData should be recognized, so I'm at a loss as to what the problem is here...
You have to retain the object:
responseData = [NSMutableData dataWithLength:0];
[responseData retain];
But that's not the common way of doing this. Simply use alloc/init:
responseData = [[NSMutableData alloc] init];
But don't forget to release in dealloc:
[responseData release];
Related
I am making an asynchronous call to the server from the client, and printing data on the server.
But the response which i get at the client is coming as null
My response retrieval part is as under:-
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
NSLog(#"connection did receive response");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere
if(!receivedData)
{
receivedData = [NSMutableData data];
}
[receivedData appendData:data];
NSLog(#"connection did receive data");
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// receivedData is declared as a method instance elsewhere
NSString *responseFromServer = [[NSString alloc]initWithData:receivedData encoding:NSUTF8StringEncoding];
NSLog(#"connection did finish load");
NSLog(#"response from the server=%#", responseFromServer);
}
So 'responseFromServer' value is coming as null.
Can anybody tell me where am i going wrong.
Thanks in advance.
If you're not strongly referencing receivedData, you may want to start doing it
receivedData = [NSMutableData data]; is an autoreleased object. Make sure that receivedData is a strong or retained property, then set it like so self.receivedData = [NSMutableData data];
It's a possibility that receivedData is getting autoreleased before the final connectionDelegate method fires, thus the responseString is using nil data in the initWithData method
Are you sure that receivedData contains UTF8 encoded data = string? initWithData:encoding: documentation ...
Return Value
An NSString object initialized by converting the bytes in data into Unicode characters using encoding. The returned object may be different from the original receiver. Returns nil if the initialization fails for some reason (for example if data does not represent valid data for encoding).
... as you can see, it returns nil if initialization fails.
I have an app with four tabs. In each tab I connect to remote server using nsurlconnection, fetches the response and display accordingly. While testing the app, I get crashes randomly. If I try to reproduce the crash again I do not get crash. I do not understand what is root cause of the crash. I enable NSZombie,symbolicated crash logs,checked the memory leak but no luck.
I started the project in Xcode 3 and now I imported same project to Xcode 4.2, so are there any issues with compatibility of Xcode?
And I use the same name for nsurlconnection in all tabs like
In Tab 1 I defined nsurlconnection as conn and Tab 2 defined nsurlconnection as conn.
Does this definition causes any issue?
Please help me solve this random crashes
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: url];
conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(label != nil){
progressView = [[ProgressView showHUDAddedTo:self.tabBarController.view animated:YES] retain];
progressView.labelText = label;
}
[request release];
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"didReceiveresponse");
if ([response isKindOfClass: [NSHTTPURLResponse class]]) {
if([(NSHTTPURLResponse *)response statusCode] == 200){
}
else{
//show Connection Error Alert
}
}
responseData = [[NSMutableData alloc]init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"didReceiveData");
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[progressView hide:YES];
NSLog(#"didFail");
//show failed alert
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"didfinish loading");
if([responseData length] > 0)
{
//handles response data
}
}
My guess without seeing code, would be that in a tab your making an NSURLConnection and doing something with the result when it completes. If you change tab before the result is returned then it is causing it to crash.
You need to cancel the NSURLConnection when viewDidDisappear, or make sure that whatever code is run on completion of it doesn't contain anything that would cause a crash if the tab isn't visible (like setting a labels text).
The way I handle this, is have a separate class that performs the URL requests that sends a notification when it's complete. That way in your viewDidAppear method you set your viewController to listen for the notifications, and in the viewDidDisapper method you stop listening for the notifications. So if your view isn't visible when the URL request finishes, the notification is fired but nothing happens.
Could you provide the output of the console? It seems not to be an error from Xcode.
These type of error usually appear when you try to access a deallocated object.
i am sure you have tried Instruments with memory leak. give a try with instruments with zombie tool, you can find it easily in instruments library.
run your code with this tool and if this crash is because of any zombie object then you will easily able to detect the location.
it has helped me few times.
SOLVED: the filename was an autoreleased string no longer available when called at createFileAtPath:
I'm trying to track the progress of the download of a file and the code I'm trying to implement is this (edited):
connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (connection)
receivedData = [[NSMutableData data] retain];
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
totalBytes = [[NSNumber numberWithLongLong:[response expectedContentLength]] intValue];
NSLog(#"content-length: %i bytes", totalBytes);
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
int resourceLength = [[NSNumber numberWithUnsignedInteger:[receivedData length]] intValue];
NSLog(#"receivedData length: %i", resourceLength);
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
[fileMgr createFileAtPath:filename contents:receivedData attributes:nil];
//if instead i write only the Apple example:
//NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
//there's no SIGABRT
[receivedData release];
[connection release];
}
but the createFileAtPath:contents:attributes: just gives SIGABRT:
-[__NSCFData
getFileSystemRepresentation:maxLength:]:
unrecognized selector sent to instance
0x2ba510
* Terminating app due to uncaught exception
'NSInvalidArgumentException', reason:
'-[__NSCFData
getFileSystemRepresentation:maxLength:]:
unrecognized selector sent to instance
0x2ba510'
what am i doing wrong? isn't this the way to download a file asynchronously ?
2 more things: a) the content-length NSLog is correct. b) if I don't initWithCapacity:content-lenght in didReceiveResponse and just init, the receivedData length only grows about two times the content-length...
SOLVED: the filename was an autoreleased string no longer available when called at createFileAtPath:
I can't possibly say it any better than in the Apple docs so I won't try. Follow through this guide and you should be solved.
As a side note there are a few memory problems in your code in this line
self.receivedData = [[NSMutableData alloc] initWithCapacity:[response expectedContentLength]];
You are creating an NSMutableData object with a retain count +1 and assuming you have appropriate getters/setters made using #property (nonatomic, retain) NSMutableData *recievedData that will add an additional +1.
To resolve this either one of these two will work (the non auto released version is best for iphone).
NSMutableData *recievedData = [[NSMutableData alloc] initWithCapacity:[response expectedContentLength]];
self.recievedData = recievedData;
[recievedData release]; recievedData= nil;
// or the autoreleased version
self.recievedData = [NSMutableData dataWithCapacity:[response expectedContentLength]];
Additionally I would call
self.recievedData = nil;
instead of
[self.recievedData release];
As it seems a bit safer and you are using the setter which is a good practice to get into.
I have a doubt regarding downloading data from a web service. One way is to download it in a single line mentioned below.
NSString *returnString = [[NSString alloc] initWithData:[NSURLConnection sendSynchronousRequest:urlrequest returningResponse:nil error:nil] encoding:NSUTF8StringEncoding];
And the other way to get it is via connectionDidFinishLoading
[..]
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:urlrequest delegate:self];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest: urlrequest returningResponse: &response error: nil];
if( theConnection )
{
webData = [[NSMutableData data] retain];
}
else
{
NSLog(#"theConnection is NULL");
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR with theConenction");
[connection release];
[webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length: [webData length] encoding:NSUTF8StringEncoding];
}
Is there any difference between these two? NSURLConnection delegate method is not called when I use a separate class to parse the response.
You're using sendSynchronousRequest:returningResponse:error: which does not call any delegates methods because it doesn't need to: when you call it, the main thread stops until the request is finished and you get the response.
If you want to make an asynchronous request, use connectionWithRequest:delegate:. I recommend to always do asynchronous responses since the synchronous request blocks the main thread and your UI can't respond during that time. Animations will become interrupted. Scrolling becomes jerky. If you do want to use synchronous requests you should do it in a background thread.
The -sendSynchronousRequest:returningResponse:error: method blocks the main-thread (whenever it runs on the main-thread of course, since it's possible to run this method from any other thread, but I believe this is not recommended).
The methods using the delegates are asynchronous, the methods will fire and the results will (at some point in the future) be returned in the delegate methods. This gives the user a more smooth experience, since the main-thread will not be blocked.
Edit: personally I hardly ever use the -sendSynchronousRequest:returningResponse:error: method for the aforementioned reasons. Most of the time I use this method when I need to build something quickly, for example a proof-of-concept. I guess one could use the method for small downloads, yet if a timeout occurs (because for some reason the server is down) the whole UI will be blocked for (I believe) 2 minutes, which would be very annoying for the enduser.
An excellent demonstration to clarify your doubt is available in apple sample apps.You can refer Apple's sample app for a better understanding of asynchronous request and parsing data in separate class.
I'm not really sure why my code is throwing a EXC_BAD_ACCESS, I have followed the guidelines in Apple's documentation:
-(void)getMessages:(NSString*)stream{
NSString* myURL = [NSString stringWithFormat:#"http://www.someurl.com"];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:myURL]];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
} else {
NSLog(#"Connection Failed!");
}
}
And my delegate methods
#pragma mark NSURLConnection Delegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
// release the connection, and the data object
[connection release];
[receivedData release];
}
I get an EXC_BAD_ACCESS on didReceiveData. Even if that method simply contains an NSLog, I get the error.
Note: receivedData is an NSMutableData* in my header file
Use NSZombieEnabled break point and check which is the freed object.
Also check:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if ([response expectedContentLength] < 0)
{
NSLog(#"Connection error");
//here cancel your connection.
[connection cancel];
return;
}
}
I have followed the guidelines in Apple's documentation:
That is not true. In both of the following, you break the rules:
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
// release the connection, and the data object
[connection release];
[receivedData release];
}
In both cases, you do not obtain the connection object with alloc, a method beginning with new or containing copy. You do not own connection in these methods. You must not release it in these methods.
It seems to me slightly dodgy that you are releasing receivedData there too. I suggest you immediately set the instance variable to nil after you release it.
[receivedData release];
receivedData = nil;
That way, it won't get accidentally released moere than once.
If you're getting the error on didRecieveData regardless of the code inside it, it looks like your delegate has been freed?
I'd check that the object that contains the getMessages method isn't being released (or autoreleased) before the connection has dfinished getting data.
EDIT: The comments below show that my above answer is wrong :)
The problem was in the recievedData variable - it was being released early. Mark suggests releasing it in the dealloc method of the object that creates the connection so he deserves all the credit for this!
There's one slight thing to lookout for there - if you release the recievedData in the dealloc method, you will leak memory if you call getMessages more than once. You will need to change getMessages slightly to this :
...
if (theConnection) {
[recievedData release]; // If we've been here before, make sure it's freed.
receivedData = [[NSMutableData data] retain];
} else {
...
I got the same error when debugging with device although there was no problem in simulation. Adding the following line of code after releasing receivedData solved the problem:
receivedData = nil;
Commenting on JeremyP, where he says that "In both of the following, you break the rules": Sheehan Alam is following Apple's code (actually, cut'n'paste) found here.
I'd also like to add (and this is something that wasn't well answered here) that the 'build and analyze' flags a "potential leak" on the NSURLConnection (which is initiated with a "[NSURLConnection alloc]"). But if one puts in a [theConnection release] on the NSURLConnection, in the same method, it will crash.
So we have something that seems to defy the 'rules' for memory management, yet works (afaik) and is in Apple's documentation..
I got EXC_BAD_ACCESS on Asynchronous call at NSURLConnection.
The code is generated by http://www.sudzc.com
I needed to add a retain to
receivedData = [[NSMutableData data] retain];
and the callback methods doesn't get bad access signal anymore.
if I add the
if ([response expectedContentLength] < 0)
{
NSLog(#"Connection error");
//here cancel your connection.
[connection cancel];
return;
}
than all my webservices are canceled, otherwise works perfectly.
While it doesn't answer the full question, I've run into this error a couple of times because I set the request's HTTPBody to an NSString instead of a NSData. Xcode tried to warn me.