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.
Related
I have been searching for this problem on the SOF for several days and I still have not found the solution (say the same problem) yet.
I'm making and app that downloads 5 images simultaneously in an URL list (each image is on a different server).
I have an ImageDownloader class subclasses NSOperation and implements the NSURLConnectionDataDelegate.
So that I can add an instance of ImageDownloader to an operationQueue in the ViewController and it will run in a separate thread under the operationQueue. The line that add the downloader to the operationQueue is here:
downloader = [[ImageDownloader alloc] init];
[downloader downloadImageWithURL:[controller.URList objectForKey:[NSString stringWithFormat:#"%d",downloadIndex]] queue:queue andTag:downloadIndex + 100]; //my custom initialize
downloader.delegate = self;
[queue addOperation:downloader]; //use the addOperation method
Everything works fine in iOS6 but messed up in iOS5 (5.0 on my test device and 5.1 on my SDK), it just doesn't receive any response nor data by performing the methods didReceiveResponse and didReceiveData at all (these 2 methods are not jumped in).
After the timeout was exceeded, the runloop jumps into didFailWithError method and the program stalls.
As I understand, this means the runloop still runs right?
I tried to print out the error and all I got is: The request timed out.
When I reduce the number of downloading instances to 2 then it runs, but not with >=3 downloading instances.
One more information is that my network connection does limit the number of connection. But it work fine in iOS6, why it just doesn't work on iOS5?
I can still load the web in the simulator while the app is downloading.
So what kind of problem is this and how can I get over this problem?
Thanks in advance.
*Update:* as there are many classes and the problem's not been clearly detected yet, I will share here the whole project. You can download it directly from here:
DownloadingImage
As I just found out, if you're using credentials there is a chance that the server will reject them randomly every once in a while. So if you have a check to make sure previousFailureCount == 0 then you will most likely have a bug.
I've just figured out where my problem is, but not really understand why.
In my ImageDownloader class, I set up a runloop with done and currentRunLoop variables.
In the main method, I have a while loop for forcing the currentRunLoop run.
As I remove those "runLoop" stuffs, the app runs smoothly on both iOS6 and iOS5.
So change the entire ImageDownloader.m with these lines then it works (I commented out some useless (say harmful) lines):
//
// ImageLoader.m
// DownloadImagesTableView
//
// Created by Viet Ta Quoc on 6/25/13.
// Copyright (c) 2013 Viet Ta Quoc. All rights reserved.
//
#import "ImageDownloader.h"
#implementation ImageDownloader
#synthesize downloadData,delegate,queue,done,customTag;
NSRunLoop *currentRunLoop;
-(void)downloadImageWithURL:(NSString *)imageUrl queue:(NSOperationQueue*)opQueue andTag:(int)tag{
self.customTag= tag;
self.queue = opQueue;
// self.done = NO;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:imageUrl] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection start];
// currentRunLoop = [NSRunLoop currentRunLoop];
NSLog(#"Start downloading image %d...",customTag);
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"Received response...");
downloadData=[[NSMutableData alloc] initWithLength:0];
expectedDataLength=[response expectedContentLength];
NSLog(#"Image %d size: %lld kb",customTag,[response expectedContentLength]/1024);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
float receivedLenght = [data length];
receivedDataLength=(receivedDataLength+receivedLenght);
float progress=(float)receivedDataLength/(float)expectedDataLength;
[delegate updateProgess:progress andIndex:[NSIndexPath indexPathForRow:customTag-100 inSection:0]];
[self.downloadData appendData:data];
// NSLog(#"Percentage of data received of tag %d: %f %%",self.customTag,progress*100);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[delegate finishedDownloadingImage:downloadData andTag:customTag];
// done = YES;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Warning" message:#"Network Connection Failed?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:nil, nil];
// NSLog(#"%#",[error debugDescription]);
NSLog(#"Connection failed! Error - %# %#",[error localizedDescription],[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
[alert show];
}
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
NSLog(#"Got here *(*&(**&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&");
}
-(void)main{
// do{
//// NSLog(#"Running....1");
// [currentRunLoop runUntilDate:[NSDate distantFuture]];
// // [currentRunLoop run];
// } while (!done);
// [currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
#end
Thank you guys for your supports.
==================================================================================
P/s: for anyone who interested in this problem, I update here my entire solution: DownloadImage_Final
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.
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 am trying to speed up my application download speed. I used Asynchronous NSURLConnection to download contents from the server, it was working fine with one connection.
I use the code from this post to implement multiple delegate objects. Multiple NSURLConnection delegates in Objective-C
When I created 2 NSURLConnection objects, each one is trying to download different files.
The callback didReceiveData routine was called but the it only received data of the first NSURLConnection object until the first connection was done then it started to receive the data from the second NSURLConnection. I want these two connections to receive data at the same time,what should I do? Here is my current code.
-(IBAction) startDownloadClicked :(id) sender
{
while (bDownloading)
{
int nCurrentCon = 0;
while (nCurrentCon < 2)
{
[self downloadAFile:[filenameArray objectAtIndex:nCurrentCon]];
nCurrentCon++;
}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
}
- (void) downloadAFile: (NSString*) filename
{
NSString* urlstr = #"ftp://myftpusername:password#hostname";
NSURLRequest* myreq = [NSURLRequest requestWithURL:[NSURL URLWithString:urlstr]];
DownloadDelegate* dd = [[DownloadDelegate alloc] init]; //create delegate object
MyURLConnection* myConnection = [[MyURLConnection alloc] initWithRequest:myreq delegate:dd
startImmediately:YES];
}
Then in my Delegate Object, I implemented these routines
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receiveBuffer setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"receiving data for %#", targetFileName); //the file name were set when this delegate object is initialized.
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Download Failed with Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"File %# - downloaded.", targetFileName);
}
Your code looks okay. I have a similar setup that works successfully (although there seems to be a limit of four concurrent conections).
The main difference between your and my code is that you use FTP while I use HTTP. Why don't you try it with HTTP connections just to see whether you have run into a restriction of FTP connections on the iPhone?
how i get the URL inside the following method ??
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection
Hey there's a comment from Mihai Damian that worked for me:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSURL *myURL = [[connection currentRequest] URL];
Cheers
You ought to be able to do theConnection.request.URL, but you can't. Annoying, isn't it?
The simplest way is to just save the URL (or the whole NSURLRequest) that you were loading. If you're using multiple connections, you can store them in a dictionary. Note that -[NSMutableDictionary setObject:forKey:] copies keys, and NSURLConnections are not copyable; the workaround is to use CFDictionarySetValue instead:
CFDictionarySetValue((CFMutableDictionaryRef)dict, connection, request);
Of course the above answers work, and I am looking for similar solution.
Just found that NSLog([connection description]); prints something like:
< NSURLConnection: 0x9129520, http://google.com>
So it is possible to parse the string returned by [connection description], and get the url from the connection, though it is kind of dirty.
You can get URL like this
- (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:NSURLErrorFailingURLStringErrorKey]);
}
for more information you van read here.
Here is my suggestion
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.rssFeedConnection = nil;
NSLog(#"connectionDidFinishLoading url : %# ", connection.originalRequest.URL);
}
In Swift 2.0 iOS 9 you can do it like:
func connectionDidFinishDownloading(connection: NSURLConnection, destinationURL: NSURL) {
print(connection.currentRequest.URL!)
}