I am using ASIHTTPRequest on the iPhone to make a call to the server, which then sends a response containing a JSON file. I got it working on a basic level, but when I added it to my project, and made a call to the server, my app started crashing with bad_access.
I make the call from viewDidLoad as such:
requestModel = [[RequestModel alloc]init];
NSURL *url = [[NSURL URLWithString:#"myURL"]retain];
[requestModel eventsRequestFor:#"myUsername" password:#"myPassword" forURL:url];
This calls a function in another class called RequestModel, and I go through and try to unpack the returned array as such (a lot of code):
-(void)eventsRequestFor:(NSString*)username password:(NSString*)password forURL:(NSURL*)forURL {
//use ASIHTTPRequest to post to sever. Here you authenticate and recieve answer
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:forURL];
[request setUsername:username];
[request setPassword:password];
[request setDelegate:self];
[request setDidFailSelector:#selector(uploadFailed:)];
[request setDidFinishSelector:#selector(uploadFinished:)];
[request startAsynchronous];
[request release];
NSLog(#"Model called");
}
#pragma mark Model Delegates
- (void)uploadFinished:(ASIHTTPRequest *)request {
// Use when fetching text data
NSLog(#"upload start");
NSString *stringFromRequest = [[NSString alloc]init];
stringFromRequest = [request responseString];
[self buildArrayFromRequest:stringFromRequest];
//[stringFromRequest release];
}
- (void)uploadFailed:(ASIHTTPRequest *)request {
NSString *statusMessage = [request responseStatusMessage];
NSLog(#"%#",statusMessage);
NSError *error = [request error];
NSLog(#"%#",error);
}
-(void)buildArrayFromRequest:(NSString*)string {
NSArray *arrayFromData = [[NSArray alloc]init];
arrayFromData = [string yajl_JSON];
NSLog(#"This is the array from the JSON %#",[arrayFromData objectAtIndex:0]);
NSMutableArray *events = [[NSMutableArray alloc] init];
for(int i= 0; i < [arrayFromData count];i++)
{
/////code to unpack array
}
///here i try to populate table with unpacked array
FirstViewController *firstVC = [[FirstViewController alloc]init];
[firstVC createTableWithArray:events];
[firstVC release];
[arrayFromData release];
[events release];
}
Any ideas on where I am going wrong? It seems like I am releasing an object that has already been released, but I can't find it.
EDIT: You're releasing an NSData that's not yours.
You don't need this line :
[arrayFromData release];
because of this mistake :
You have put :
NSArray *arrayFromData = [[NSArray alloc]init];
arrayFromData = [string yajl_JSON];
when all you need is this :
NSArray *arrayFromData = [string yajl_JSON]; // This doesn't need releasing
You're releasing a request that's already autoreleased :(
// Create an autoreleased request object
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:forURL];
...
[request release]; // fail here - this line is not needed
Just FYI :
This is unneeded code :
NSString *stringFromRequest = [[NSString alloc]init];
stringFromRequest = [request responseString];
You are making a string object and then getting a different one. The first one you're making will be leaked. This code will do what you want :
NSString *stringFromRequest = [request responseString]; // This doesn't need releasing :)
However, it's the cause of a leak, not the crash that you are reporting.
(You've got this pattern in other places in your code, not just here.)
Related
I am making call to a server requesting JSON data using NSURLConnection.
For some reason I get part of the response. If I hit the url through the browser the response its correct. The weird thing is that it happens only sometime. So I'm having a hard time debugging the issue.
Then when because the response is not complete I get the following error:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (Invalid value around character 0.) UserInfo=0xa4634a0 {NSDebugDescription=Invalid value around character 0.} {
NSDebugDescription = "Invalid value around character 0.";
}
I guess it could also be an issue with the server it self. Here's my code:
-(void) getShareHistory:(NSString *)range paging:(NSInteger *)page{
NSString *post = [NSString stringWithFormat:#"range=%#&paging=%#",
range,
[NSString stringWithFormat:#"%ld",(long)page]];
NSString *url = [NSString stringWithFormat:#"http://www/domai.com/handle_share_links.php?action=history"];
NSData *post_data = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [post_data length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setCachePolicy:NSURLRequestUseProtocolCachePolicy];
[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:post_data];
self.shareHistoryConn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)response{
NSString *strData = [[NSString alloc]initWithData:response encoding:NSASCIIStringEncoding];
NSLog(#"response %#",strData);
NSError *jsonParsingError = nil;
if(connection == self.shareHistoryConn)
{
NSArray *data = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingAllowFragments error:&jsonParsingError];
if(!jsonParsingError)
{
[[self delegate] onGetShareHistorySuccess:data];
}else{
[[self delegate] onGetShareHistoryFailed:jsonParsingError];
}
}
Thanks in advance.
What you're seeing is normal behavior. didReceiveData can be called any number of times. It is up to you to keep accumulating the data until you get connectionDidFinishLoading.
The standard delegate structure is like this:
- (void) connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response {
// connection is starting, clear buffer
[self.receivedData setLength:0];
}
- (void) connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data {
// data is arriving, add it to the buffer
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection*)connection
didFailWithError:(NSError *)error {
// something went wrong, clean up interface as needed
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// all done, we are ready to rock and roll
// do something with self.receivedData
}
Always implement all four delegate methods.
How it do so that I will not need to put data object right in selector call method?
code with warning (Local declaration of data) :
NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:#"SiluetaImage", #"ACTION", silueta_id, #"siluetaid", siluetaTyp_id, #"siluetatypid", nil];
[self performSelector:#selector(downloadBinDataForTyp:data:)
withObject:#"SiluetaImage"
withObject:data];
code without warning :
[self performSelector:#selector(downloadBinDataForTyp:data:)
withObject:#"SiluetaImage"
withObject:[NSDictionary dictionaryWithObjectsAndKeys:#"SiluetaImage", #"ACTION", silueta_id, #"siluetaid", siluetaTyp_id, #"siluetatypid", nil]];
the selector:
- (void)downloadBinDataForTyp:(NSString *)typ data:(NSDictionary*)data
{
ASINetworkQueue *q = [self queue];
NSString *sUrl = #"url_web_service";
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:sUrl]];
NSString *msgLength = [NSString stringWithFormat:#"%d", [[self getMsg] length]];
[request setTimeOutSeconds:60];
[request addRequestHeader: #"Content-Type" value:#"text/xml; charset=utf-8"];
[request addRequestHeader: #"SOAPAction" value:_action];
[request addRequestHeader: #"Content-Length" value:msgLength];
[request setRequestMethod: #"POST"];
request.userInfo = data;
[request appendPostData:[self getMsg]];
[request setDelegate:self];
[q addOperation:request];
[q go];
}
NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:#"SiluetaImage", #"ACTION", silueta_id, #"siluetaid", siluetaTyp_id, #"siluetatypid", nil];
[self performSelector:#selector(downloadBinDataForTyp:data:)
withObject:#"SiluetaImage"
withObject:data];
In this you are passing data whose scope is just to the method. And outside the method it can not be accessed, as it is getting released.
I am trying to send a post value to my server.
It successfully send English characters.
This code is for finding friend, and it can't send UTF-8 text.
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope{
/*
Update the filtered array based on the search text and scope.
*/
if(![searchText isEqualToString:#""]){
NSUserDefaults * userdef = [NSUserDefaults standardUserDefaults];
NSString *encodedSearchText = [searchText stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString * uploadUrl = [NSString stringWithFormat:#"%#/%#" , GPON_URL , GPON_URL_SEARCHFRIEND];
NSURL *url = [NSURL URLWithString:uploadUrl];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:encodedSearchText forKey:#"u_id"];
[request setPostValue:[userdef objectForKey:KEY_USER_ID] forKey:#"s_username"];
[request startSynchronous];
[request setShouldAttemptPersistentConnection:NO];
NSString *response = [request responseString];
SBJsonParser * parser = [[[SBJsonParser alloc] init] autorelease];
NSDictionary * json = (NSDictionary *)[parser objectWithString:response];
[searchMember removeAllObjects];
if( [[json objectForKey:#"result"] isEqual:#"YES"] ){
[searchMember addObjectsFromArray:[json objectForKey:#"data"]];
[self.tableView reloadData];
}
}
}
here is my code. can you see what cause the error?
You don't appear to be setting the string encoding of the request; try:
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setStringEncoding:NSUnicodeStringEncoding];
Also, this is a very poor way of determining if an NSString is empty:
if (![searchText isEqualToString:#""])
Use the length method instead:
if ([searchText length] > 0)
I have a custom NSObject class called "GREST", everything works aside from this method. Auto completion won't even recognize it, and I honestly can't figure out why. I've tried changing the method's name and everything. When I call the method, it works, I just get the warning that the class may not respond to this method.
I have this method declared in my header file
- (void)connect:(NSString *)connection_url parameters:(NSString *)parameter_string header:(NSString *)header_type
And here is the implementation
- (void)connect:(NSString *)connection_url parameters:(NSString *)parameter_string header:(NSString *)header_type {
NSData *request_data = [parameter_string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *request_length = [NSString stringWithFormat:#"%d", [request_data length]];
response_data = [[NSMutableData alloc] init];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:connection_url]];
[request setHTTPMethod:#"POST"];
[request setValue:request_length forHTTPHeaderField:#"Content-Length"];
if([header_type isEqualToString:#""] || header_type == nil || header_type == NULL) {
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
} else {
[request setValue:header_type forHTTPHeaderField:#"Content-Type"];
}
[request setHTTPBody:request_data];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setTimeoutInterval:10];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
if(!connection) {
[self apicall_failed];
}
[connection release];
}
When I try to call this method, XCode gives me the following warning
'GREST' may not respond to '-connect:parameter_string:header_type:'
Edit, here's how the code is being called:
GREST *api = [[GREST alloc] init];
[api setDelegate:self];
[api connect:#"http://domain.com" parameter_string:#"{'username':'hi'}" header_type:#"application/json"];
[api release];
Everything works fine, but I'm just trying to get rid of this warning now. Any ideas on how I can do this? Thanks in advance!
compare the method names:
'-connect:parameter_string:header_type:'
to
'-connect:parameters:header:'
the difference is that you are using the names of the method's arguments, not the actual method/selector. this will result in a runtime exception when called.
the compiler will tell you the line where you try to use the former, and xcode will probably highlight the line.
Change
[api connect:#"http://domain.com" parameter_string:#"{'username':'hi'}" header_type:#"application/json"];
to
[api connect:#"http://domain.com" parameters:#"{'username':'hi'}" header:#"application/json"];
I was make communication for client-server application and have strange problem.
here is a code where i pickup objects.
- (byref NSArray*)objectsOfName:(bycopy NSString*)name
withPredicate:(bycopy NSPredicate*)predicate;
{
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:name
inManagedObjectContext:context]];
[request setPredicate:predicate];
NSArray *results = [context executeFetchRequest:request error:&error];
[request release], request = nil;
if (error) {
NSLog(#"%#:%# Error on fetch %#", [self class], NSStringFromSelector(_cmd), error);
return nil;
}
//NSLog(#"%#:%# Result of fetch is %#", [self class], NSStringFromSelector(_cmd), results);
return results;
}
Here is pickup:
NSArray *destinations;
#ifdef SNOW_CLIENT
destinations = [server objectsOfName:#"DestinationsListWeBuy" withPredicate:predicate];
If i do
NSLog(#"Destination:%#\n",destinations);
i seen all objects in log.
If i try to do
NSLog(#"all:%#\n%#\n%#\n",[[destinations objectAtIndex:0] valueForKey:#"rate"],[[destinations objectAtIndex:0] valueForKey:#"lastUsedACD"],[[destinations objectAtIndex:0] valueForKey:#"lastUsedCallAttempts"]);
i seen attributes also.
But, if i try to do loop around objects:
for (NSManagedObject *dest in destinations)
{
NSLog(#"all:%#\n%#\n%#\n",[dest valueForKey:#"rate"],[dest valueForKey:#"lastUsedACD"],[dest valueForKey:#"lastUsedCallAttempts"]);
i have EXC_BAD_ACCESS in this part of code:
for (NSManagedObject *dest in destinations)
all debug technic, which i know, don't give me possibility to understand, what happened. (NSZombieEnabled = YES)
if i do loop at another manner:
for (NSUInteger count = 0;count < [destinations count]; count++)
NSLog(#"all:%#\n%#\n%#\n",[[destinations objectAtIndex:count] valueForKey:#"rate"],[[destinations objectAtIndex:count] valueForKey:#"lastUsedACD"],[[destinations objectAtIndex:count] valueForKey:#"lastUsedCallAttempts"]);
i seen all keys without exception. All nsmanagedobject's is subclassed.
If i need implement encodeWithCored method for all subclasses, examples is appreciated.
*UPDATE for Marcus *
This is how i receive objects from server side:
- (byref NSArray*)allObjects
{
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:#"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
[dict setValue:#"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
NSError *error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:coordinator];
[moc setUndoManager:nil];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(mergeChangesForClient:)
name:NSManagedObjectContextDidSaveNotification
object:thirdMOC];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Carrier"
inManagedObjectContext:moc];
[request setEntity:entity];
[request setIncludesSubentities:YES];
NSError *error = nil;
NSArray *objects = [moc executeFetchRequest:request error:&error];
[request release], request = nil;
for (NSManagedObject *carrier in objects) {
NSSet *destinations = [carrier valueForKeyPath:#"destinationsListForSale"];
for (NSManagedObject *destination in destinations) [destination addObserver:self forKeyPath:#"rate" options:NSKeyValueObservingOptionNew context:nil];
}
if (error) {
NSLog(#"%#:%# error: %#", [self class], NSStringFromSelector(_cmd), error);
return nil;
}
return objects;
}
This is what i do with them on client side:
NSArray *allObjects = [server allObjects];
[carrierArrayController setContent:allObjects];
There is no serialization in this case. Any other ways (like send copy of server moc to client side doesn't work, it just generate exceptions on main.c).
p.s. many thanks to Marcus for his Core Data book.
unrecognized selector sent to class 0x1000a2ed8 2011-03-17 02:15:18.566 snowClient[19380:903] +[AppDelegate encodeWithCoder:]: unrecognized selector sent to class 0x1000a2ed8
That is not a core data problem. That is an error in your code where you are trying to call a method on an object that does not respond to that method. You need to track that down as it appears that you are trying to serialize your AppDelegate.
Update
What kind of class is 0x1000a2ed8? Break on the exception and print out the object to see what it is. Again, this is not a core data error directly, it is sending a messages to an object that does not respond to that message. It is possible that Core Data no longer allows you to send Managed objects across as distributed objects. It is possible that this is simply an issue with an over-released object. No way to know without further investigation.
Step one: Find out what object 0x1000a2ed8 is and see if the object changes from one run to the next.