Data gets deallocated before use with ASIHTTPRequest asynchronous mode - iphone

I'm using ASIHTTPRequest in asynchronous mode in a function called from my viewDidLoad()
NSURL *url = [NSURL URLWithString:#"http://somewebsite.com/data.txt"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
This is my requestFinished() where I do some text replacement and populate an NSArray with the data I've received from the website. It's just text, one data item per line.
The NSArray (and alloc / init) in viedDidLoad().
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSString *sTemp = [request responseString];
sTemp = [sTemp stringByReplacingOccurrencesOfString:#"\n" withString:#","];
arrayTidalData = [sTemp componentsSeparatedByString:#","];
NSLog(#"Loaded %d items into the array",[arrayTidalData count]);
[tableTideTimes reloadData];
}
NSLog reports 127 data items.
Now, I use the NSArray data to populate a UITableView.
But, in cellForRowAtIndexPath() when I attempt to access the NSArray (arrayTidalData), for instance by doing a count, I get the following:
TideTimes[14696:b303] * -[__NSArrayM count]: message sent to deallocated instance 0x4e6c6a0
(I turned on NSZOMBIEEnabled = YES to get this data)
It seems the NSArray has been deallocated before I can use it. I also tried populating an NSString with the data in requestFinish() but got the same result.
Am I missing something really simple or am I doing something terribly wrong?
(It's my first time with ASIHTTPRequest)

Replace
arrayTidalData = [sTemp componentsSeparatedByString:#","];
with
arrayTidalData = [[sTemp componentsSeparatedByString:#","] retain];
It is because componentsSeparatedByString: returns autoreleased object. So it is released after method requestFinished: ends working.
And don't forget to release at the end of work (for example, dealloc).

Related

how to cache xml data on iphone

I am trying to use the ASIDownloadCache from the ASIHTTPRequest library. I think I have it almost set up but the data I am printing to the log is a bunch of numbers.. I think it might be a formatting problem.. but I would like to run it past someone with more experience first to make sure I'm doing it correctly and then to hopefully help me fix the issue.
The code belows shows you how I am setting up my cache, I am using this view for several data sets, hence the need to use an if statement so that I am only setting up the cache on specific data.
- (IBAction)setRequestString:(NSString *)string
{
//Set database address
NSMutableString *databaseURL = [[NSMutableString alloc] initWithString:#"http://***.***.***.***:8888/codeData/"]; // iphone development
//PHP file name is being set from the parent view
[databaseURL appendString:string];
//call ASIHTTP delegates (Used to connect to database)
NSURL *url = [NSURL URLWithString:databaseURL];
checkDataSet = [[NSString alloc] initWithFormat:string]; //Loads ICMfg.xml into checkDataSet for setting up cache
//Create If statments here
if ([checkDataSet isEqualToString:#"ICMfg.xml"]) {
//Cache stuff goes in here
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadCache:[ASIDownloadCache sharedCache]];
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
[request setCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy];
[request setSecondsToCache:60*60*24*30]; // Cache for 30 days
[request setDelegate:self]; // A delegate must be specified
[request startSynchronous];
//[request setDidFinishSelector:#selector(requestFinished:)]; // And an appropriate
}
else
{
//this else statments lets all of the other datasets come through here
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
}
From here, when [checkDataSet isEqualToString:#"ICMfg.xml"] is true it will set the cache parameters and then calls the following method where I get everything ready to parse my information
- (void)requestFinished:(ASIHTTPRequest *)request
{
if ([checkDataSet isEqualToString:#"ICMfg.xml"]) {
BOOL success = [request didUseCachedResponse];
NSLog(#"------------>>>>>>> Success is %#\n", (success ? #"YES" : #"NO"));
responseString = [request responseString];
capturedResponseData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%#", capturedResponseData); //this prints out the weird data.
[self startTheParsingProcess:capturedResponseData];
}
else
{
responseString = [request responseString]; //Pass requested text from server over to NSString
capturedResponseData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
[self startTheParsingProcess:capturedResponseData];
}
}
From here, I check my nslog to see the result of that NSlog and it spits out a bunch of numbers, below is a small section of the output. The next step for me is to check to see if anything is actually being parsed.. and also to see if the cache is working or not.. then I need to figure out hopefully with your help how to format the data correctly if thats my main problem..
also I would like to ask how to get this working asynchronously as currently I can only get it to work synchonosly.
2011-11-09 09:29:55.216 code[3968:207] ------------>>>>>>> Success is YES
2011-11-09 09:29:55.239 code[3968:207] <3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f 64696e67 3d225554 462d3822 3f3e0d0a 3c494345 6e673e3c 52657375 6c742044 42566572 73696f6e 3d223132 33223e3c 5461626c 65733e3c 5461626c 65205461 626c654e 616d653d 2249434d 6667223e 3c526f77 733e3c52 6f77204d 414e5546 41435455 52455249 443d2237 30362220 4d414e55 46414354 55524552 3d22412d 445a4722 2049534c 4f434b4d 414e5546 41435455 5245523d 22462220 49535645 4849434c 453d2246 223e3c2f 526f773e 3c526f77 204d414e 55464143 54555245 5249443d 22333138 22204d41 4e554641 43545552 45523d22 412e522e 452e2220 49534c4f 434b4d41 4e554641 43545552 45523d22 46222049
any help would be greatly appreciated.
I don't see anything that immediately sticks out in your code as wrong.
The NSLog() is printing an NSData object, which is binary data so the hexadecimal numbers you are seeing are the representations of the bytes which is exactly what you would expect.
The NSData Class Reference:
description
Returns an NSString object that contains a hexadecimal
representation of the receiver’s contents.
(NSString *)description
Return Value
An NSString object that contains a hexadecimal representation of the receiver’s contents in
NSData property list format.
If you want to print out the string representation of this data, use:
NSString *capturedResponseString = [NSString stringWithUTF8String:[capturedResponseData bytes]];

memory leak using JSONKit when called a second time

I've read the Apples docs on memory management and feel I understand them but I can't get this to not leak. In this example I have the process running on the main thread to keep it simple. The first time search button is clicked all works fine, no leaks. Second time search is clicked/perfomed everything works find but instruments displays the following leaks:
Leaked Object # Address Size Responsible Library Responsible Frame
NSCFString,42 < multiple > 1.30 KB CTContacts jk_cachedObjects
NSCFString,16 < multiple > 464 Bytes CTContacts jk_cachedObjects
JKDictionary,7 < multiple > 224 Bytes CTContacts jk_object_for_token
Malloc 288 Bytes,7 < multiple > 1.97 KB CTContacts jk_object_for_token
Malloc 32 Bytes, 0x7859a30 32 Bytes CTContacts jk_object_for_token
JKArray, 0x78599f0 32 Bytes CTContacts jk_object_for_token
it seems to be pointing to this line: (listed as %100)
NSDictionary *resultsDictionary = [jsonData objectFromJSONDataWithParseOptions:JKParseOptionStrict error:(NSError **)error];
I've tried NSDictionary *resultsDictionary =[ [[NSDictionary alloc]init]autorelease]; but with same result.
Below are the two methods involved:
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
pickerView.hidden=YES;
searchBar.showsScopeBar=YES;
[searchBar setShowsCancelButton:NO animated:YES];
[searchBar resignFirstResponder];
[self queryWebService];
}
-(void) queryWebService{
NSString *urlAddress = [NSString stringWithFormat:#"http://myweb.com/json.php?lname=%#&searchType=%#",searchBar.text,currentSearchCategory];
NSURL *url = [NSURL URLWithString:urlAddress];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startSynchronous];
NSError *error = [request error];
if (!error){
NSString *responseString = [request responseString];
//NSLog(#"Response: %#", responseString);
NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *resultsDictionary = [jsonData objectFromJSONDataWithParseOptions:JKParseOptionStrict error:(NSError **)error];
if (resultsDictionary)
{
rows = [[resultsDictionary objectForKey:#"contacts"] retain];
resultsDictionary=nil;
}
}
[myTableView reloadData];
}
NSArray "rows" is used as the tableView dataSource. Any help would be appreciated, thanks.
I'd imagine that rows is the cause. Each time you run through the loop, you add another retain to it. Getting rid of the retain should do the trick and get rid of the memory leak. If for some reason, a retain is necessary there, you'll just have to find a place elsewhere to release it and keep your retain count at the proper value

dispatch_async memory problems in iOS

I have a loop of about 2000+ items I need to go through:
for (NSDictionary* dict in array) {
NSString *fileName = [NSString stringWithFormat:#"%#/%#_lg.jpg?t=",manufacturerID, [[dict valueForKey:#"ItemID"] stringByReplacingOccurrencesOfString:#" " withString:#"%20"]];
NSString *savePath = [documentsPath stringByAppendingPathComponent:fileName];
NSURL *url = [NSURL URLWithString: [[NSString stringWithFormat:kProductImagesURL, fileName]stringByAppendingString:lastImagesSyncDate]];
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(aQueue, ^{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
NSError *error = [request error];
if (!error) {
int statusCode = [request responseStatusCode];
if (statusCode==200) {
NSData *responseData = [request responseData];
[responseData writeToFile:[savePath stringByReplacingOccurrencesOfString:#"%20" withString:#" "] atomically:YES];
}
}
});
}
This works great and my main thread is not blocked but my memory goes through the roof - how do I get it to be released? Once the queue is empty it drops but I need it to clear out as it is going along.
Although you are running the code in the background you are running all of the code in the background at the same time. As fast you are able to loop through the array you are creating a new ASIHttpRequest that will be trying to download and save data at the same time. You may want to move your loop inside of the dispatch_async, or use an NSOperation that does the same thing but limit the max concurrent operations on the NSOperationQueue. If you move the loop inside of dispatch_async to do one at a time remember to create an NSAutoreleasePool locally and drain it periodically.
Instead of getting an autoreleased ASIHTTPRequest , try to alloc ,init , release one.
Haven't tried it , but think about alternating async and sync calls (to the same thread, not the main one) , like having 20 async requests followed by one sync.. this trick could help.

Cast JSON object (NSCFDictionary) to string and parse it

I'm working with facebook connect and trying to handle the JSON object that i'm receiving.
I invoked the requstWithGraphPath method and need to get back a JSON object,
tried to parse it and getting an error:
SBJSON *parser = [[SBJSON new] autorelease];
NSData *data = [[NSData alloc] initWithData:result];
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; -> in this line - "[__NSCFDictionary length]: unrecognized selector sent to instance"
NSArray *events = [parser objectWithString:jsonString];
What's the problem?
Can I get the string in an other way or parse the object differently?
Thanks.
If you are working with the delegate callback
- (void)request:(FBRequest *)request didLoad:(id)result;
the parsing work has been done for you. Traverse the NSDictionary or NSArray to find the data you are looking for. If you are working with the delegate callback
- (void)request:(FBRequest *)request didLoadRawResponse:(NSData *)data;
you should initialize an NSString with the data, and use the category method that SBJSON adds to NSString for creating an id. That is assuming the data is data that constructs a string.
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
id result = [jsonString JSONValue];
Are you sure the error happens on that line, or does it happen on the line above?
If result is an NSDictionary (or CFDictionary, same thing), then it is already parsed and you do not need to do that yourself — and it could cause that error message too, on the line above.
The line:
data = [[NSData alloc] initWithData:result];
is almost certainly not what you want to do, as it is equivalent to
data = [result copy];
assuming that result is an NSData object (or NSMutableData), which I'm guessing it isn't.

Why my application crash on applicationDidEnterBackground?

I have a static method called writeToServer that is called when application enter in background mode.
in my AppDelegate.m:
- (void)applicationDidEnterBackground:(UIApplication *) application {
[LogZone writeToServer];
NSLog(#"Log sended to server. Done.");
}
in my LogZone.m:
+ (void) writeToServer {
NSString *qStr = [[NSString alloc]
initWithFormat:#"%#?ip=%#&uid=%#&platform=%#&model=%#&lat=%#&lon=%#",
LOG_SERVER_URL,
_LOG_IP, _LOG_UID, _LOG_PLAT, _LOG_MOD, _LOG_LAT, _LOG_LON];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:qStr]];
[request setHTTPMethod: #"POST"];
[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
}
Uppercase vars are static strings created in this way:
.h
extern NSString* _LOG_UID;
.m
NSString* _LOG_UID = #"-1";
When I enter in background mode it crash with this "classic" error:
* -[CFString respondsToSelector:]: message sent to deallocated instance
0x6a4c800
But why?
I don't release anything!=!
What's wrong?
But why?
I don't release anything!
Sure, but do you retain the right objects?
Retaining at the right time is every bit as important as not releasing at the wrong time...
Why not just use the following:
NSString *qStr = [NSString stringWithFormat:#"%#?ip=%#&uid=%#&platform=%#&model=%#&lat=%#&lon=%#", LOG_SERVER_URL, _LOG_IP, _LOG_UID, _LOG_PLAT, _LOG_MOD, _LOG_LAT, _LOG_LON];