Memory leak that appears to be in touchJSON - ios

Here's a leak problem that I'm having trouble with. Most of this code is just here for context so you can see that the "response" NSData object is not what's leaking.
If I drill down into the touchJSON code, following the stack trace as given to me by the LEAKS tool, the leak apparently begins life at the line
*outStringConstant ....
But since this is such a commonly used library, I doubt it's the problem.
One note. This doesn't leak the first time it's executed, only every subsequent time. But it leaks a lot, so the response data is probably the actual data that's leaking.
Also, if anyone is familiar with touchJSON and this code, can you explain to me what this outStringConstant variable is and what it does? It doesn't appear to play any role, other than to be assigned a copy of theString, though if I remove that line the code crashes.
MY CODE is
dataRequestURL = [NSString stringWithFormat:#"http://www....", ...];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:dataRequestURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:&ts_response error:&ts_error];
NSArray *array = [[CJSONDeserializer deserializer] deserialize:response error:nil]; <- LEAKS HERE
TOUCHJSON CODE is
-(BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError {
NSMutableString *theString = [[NSMutableString alloc] init];
if (outStringConstant != NULL) { *outStringConstant = [[theString copy] autorelease]; }
[theString release];
}

Related

Memory issue generated in iPad because data receiving very lengthy

I am struck in a big problem. What I am trying to do is getting data from the server but the server in a single hit give all the data which is very large in quantity and I am doing all my process on the main thread, So there are around 400-500 images in the form of URL which I am saving in document directory in the form of NSData. So in the dubug navigator when the memory consumption reached around 80-90 mb then my application crashed and showing the following error:-
mach_vm_map(size=135168) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
2015-01-23 17:10:03.946 ArchaioMobileViewer[853:148470] *** Terminating app due to uncaught exception 'NSMallocException', reason: 'Attempt to allocate 262144 bytes for NS/CFData failed'
I am Using ARC but still I am getting the memory problem. This is my code `-
(void)downloadDocumentsFromServer:(NSDictionary *)documentsList IsUpdate:(BOOL)isUpdate;
{
//Main Target(22)
BusinessLayer* bLL = [[BusinessLayer alloc]init];
FileManager* downloadImages = [FileManager alloc];
for(NSDictionary* inspDocumentResult in documentsList)
{
FloorDocument* floorDocument = [[FloorDocument alloc]init];
floorDocument.docID = [inspDocumentResult objectForKey:#"docID"];
floorDocument.buildingID = selectedBuildingID;
floorDocument.clientID = clientID;
NSDictionary* documentArray = [inspDocumentResult objectForKey:#"Document"];
floorDocument.docType = [[documentArray objectForKey:#"Type"] stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
floorDocument.docScale = [documentArray objectForKey:#"Scale"];
floorDocument.docDescription = [documentArray objectForKey:#"DocDesc"];
//floorDocument.floor = [bLL getFloorNameByDocIDAndBuildingID:selectedBuildingID DocID:floorDocument.docID];
floorDocument.floor = [inspDocumentResult objectForKey:#"Floor"];
NSLog(#"%#",[inspDocumentResult objectForKey:#"hiResImage"]);
[downloadImages downloadInspectionDocuments:[inspDocumentResult objectForKey:#"hiResImage"] ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID];
NSLog(#"Floor %# - High Res Image copied for %#",floorDocument.floor,floorDocument.docID);
//Download the Low Res Image
NSString* lowResImage = [inspDocumentResult objectForKey:#"lowResImage"];
[downloadImages downloadInspectionDocumentsLowRes:lowResImage ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID LowResName:#"lowResImage"];
//Copy the Quarter Size File
lowResImage = [lowResImage stringByReplacingOccurrencesOfString:#"LowRes" withString:#"LowRes4"];
[downloadImages downloadInspectionDocumentsLowRes:lowResImage ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID LowResName:#"lowResImage4"];
NSLog(#"Floor %# - Low Res Images copied for %#",floorDocument.floor,floorDocument.docID);
//Download the tiles
NSArray* tiles = [inspDocumentResult objectForKey:#"lsUrls"];
for(NSString* tile in tiles)
{
#autoreleasepool {
NSArray* tileNameArray = [tile componentsSeparatedByString:#"/"];
if(tileNameArray.count > 0)
{
NSString* destTile = [tileNameArray objectAtIndex:tileNameArray.count-1];
destTile = [destTile stringByReplacingOccurrencesOfString:[NSString stringWithFormat:#".%#",floorDocument.docType] withString:#""];
NSLog(#"TileName:%#",destTile);
[downloadImages downloadInspectionDocumentsTiles:tile ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID TileName:destTile];
}
}
}
NSLog(#"Floor %# - Tiles Image copied for %#",floorDocument.floor,floorDocument.docID);
NSLog(#"Downloading Documents Tiles For %# Completed at %#",floorDocument.docID,[bLL getCurrentDate]);
[bLL saveFloorDocuments:floorDocument IsUpdate:isUpdate];
// downloadImages=nil;
}
bLL = nil;
}
please help me out in this problem.`
This is the code which I am using inside the DownloadInspectionDocuments:-
-(void)downloadInspectionDocuments:(NSString *)url ImageName:(NSString *)imageName FileType:(NSString*)fileType Folder:(NSString*)folder
{
#autoreleasepool
{
NSString* source =[FileManager getInspectionDocumentsFolder];
//Lets get the destination folder
NSString *destination = [NSString stringWithFormat:#"%#/%#/%#",source,folder,imageName];
[self createFolder:destination CreateSubFolders:true];
NSString *filePath = [NSString stringWithFormat:#"%#/%#.%#",destination,imageName,fileType];
NSFileManager* fm = [[NSFileManager alloc]init];
if(![fm fileExistsAtPath:filePath])
{
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
[data1 writeToFile:filePath atomically:YES];
}
}
// return [NSString stringWithFormat:#"%#.%#",imageName,fileType];
}
ARC isn't garbage collection: it will insert the memory management code (retain/release) for you but you still need to make sure you are not using up too many resources (in the same way you would in non-ARC code).
You are running this large loop on the main thread, so any memory being consumed is not going to be freed until the next run loop.
You need to break this function down into smaller steps that can be carried out in stages.
For now, if there isn't too much memory consumed for a single iteration of the outer-loop of the function you can add an autorelease pool at that level (I see you have on on the inner loop)
for(NSDictionary* inspDocumentResult in documentsList)
{
#autoreleasepool {
.... remaining code goes here
}
}
and it will at least drain what it can each iteration.
Given that you are downloading a large number of files and will be relying on network connectivity I would recommend performing the downloads asynchronously though. If you haven't already, check out AFNetworking to simplify this. This will give you much more control over your resources than you are getting now with a resource-intensive blocking call on the main thread.
You can save yourself a lot of work by following davbryn's and Andrea's suggestions to use AFNetworking and stream the file. Basically, don't put the whole file in memory and then write it to disk, write to disk as you get the bytes from the network. This should reduce the pressure on memory. For example:
- (void)downloadFile:(NSString *)urlString {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
NSString *destinationPath = [NSDocumentDirectory() stringByAppendingPathComponent:#"some-file-name"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setOutputStream:[NSOutputStream outputStreamToFileAtPath:destinationPath append:NO]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Super duper awesome!");
// Maybe start another download here?
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error downloading file: %#", error);
}];
[operation start];
}
So then all you need to do is generate the list of things to download and in your success block start downloading another file.

Slow default access crash in iOS

I have been programming for 3 years on iOS, heavily using core data. However, I have never come across a crash like this before and have no idea why this is occurring. This current app is only 4 very simple view controllers, saving to nsdefaults only about 5 different times. The error I am confused on is "Slow defaults access for key ClientState took 0.037632 seconds, tolerance is 0.020000". I have noted where in my code it says the problem is in my code. Also, this is long after the view controller has loaded. This process occurs after a button press. Lastly, this crash only happens half the time, meaning there are occasions when the code actually works without a crash.
NSUserDefaults *ab = [NSUserDefaults standardUserDefaults];
NSString *frcrat = [ab objectForKey:#"frcrat"];
NSString *lapper = [alertView textFieldAtIndex:0].text;
spinner.hidden = NO;
[spinner startAnimating];
delem = NULL;
delem = [[NSMutableArray alloc] init]; //Line after this line gives error Thread 1: Exc_bad_access (code = 1, address=0xe0bb2f85)
NSString *urlString = [[NSString stringWithFormat:#"http://www.mywebsite.com/enum.php?fracat=%#&num=%#&sap=%#", frcrat, lapper, _sna]stringByAddingPercentEscapesUsingEncoding : NSUTF8StringEncoding ];
NSXMLParser *Parser = [[[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:urlString]] autorelease];
[Parser setDelegate:self];
[Parser parse];
I assume this code is placed somewhere in your view controller initialization.
The warning you're getting means that it's taking too long for the view controller to load, and this is clearly due to the initWithContentsOfURL: call in your code.
As you can read here, initWithContentsOfURL: is blocking, meaning that you should never call it on the main thread. You should perform the XML parser initialization asynchronously. Something like:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *urlString = [[NSString stringWithFormat:#"http://www.mywebsite.com/enum.php?fracat=%#&num=%#&sap=%#", frcrat, lapper, _sna]stringByAddingPercentEscapesUsingEncoding : NSUTF8StringEncoding ];
NSXMLParser *parser = [[[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:urlString]] autorelease];
[parser setDelegate:self];
[parser parse];
}
It was not even related to nsuser defaults. It was because of the string _sna. I instead saved that string in nsuserdefaults and reloaded it when I needed it as NSString *snameri. Thanks apple debugger for giving me an inappropriate error.

iOS NSURL queuing mechansim for multiple requests from file

I am very new to iOS development, but I would like to make an app that has two table view controllers (columns): both are a row of images that act as links. The first would be a column of YouTube videos and the second a column of websites. I would like to have all these listed in a file file.txt listed like so: V, http://youtube.com/example W, http://example.com
There would be a long list of those, the V meaning its a video (for the video column) and W for the websites. Now, I understand how to being the single file in, but what happens afterwards is my concern. Can I read each line into some sort of queue and then fire the NSURL request for each one consecutively? How can that be done with NSURL? Is there perhaps a better approach?
There are two questions for me:
Is a text file really the best format?
I might suggest a plist or archive (if the file is only going to exist only in your app's bundle and/or documents folder) or JSON (if it's going to live on a server before delivering it to the user) instead of a text file. It will make it easier to parse this file than a text file. For example, consider the following dictionary:
NSDictionary *dictionary = #{#"videos" : #[#"http://youtube.com/abc", #"http://vimeo.com/xyz"],
#"websites": #[#"http://apple.com", #"http://microsoft.com"]};
You can save that to a plist with:
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"files.plist"];
[dictionary writeToFile:plistPath atomically:YES];
You can add that file to your bundle or whatever, and then read it at a future date with:
dictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
You can, alternatively, write that to a JSON file with:
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonPath = [documentsPath stringByAppendingPathComponent:#"files.json"];
[data writeToFile:jsonPath atomically:YES];
You can read that JSON file with:
data = [NSData dataWithContentsOfFile:jsonPath];
dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
Either way, you can get the list of videos or web sites like so:
NSArray *videos = dictionary[#"videos"];
NSArray *websites = dictionary[#"websites"];
Now that you have your arrays of videos and websites, the question then is how you then use those URLs.
You could do something like:
for (NSString *urlString in videos) {
NSURL *url = [NSURL URLWithString: urlString];
// now do something with the URL
}
The big question is what is the "do something" logic. Because you're dealing with a lot of URLs, you would want to use a NSOperation based solution, not a GCD solution, because NSOperationQueue lets you control the degree of concurrency. I'd suggest a NSOperation-based networking library like AFNetworking. For example, to download the HTML for your websites:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4;
for (NSString *urlString in websites)
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// convert the `NSData` responseObject to a string, if you want
NSString *string = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
// now do something with it, like saving it in a cache or persistent storage
// I'll just log it
NSLog(#"responseObject string = %#", string);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error = %#", error);
}];
[queue addOperation:operation];
}
Having said that, I'm not sure it makes sense to kick off a ton of network requests. Wouldn't you really prefer to wait until the user taps on one of those cells before retrieving it (and for example, then just open that URL in a UIWebView)? You don't want an app that unnecessarily chews up the user's data plan and battery retrieving stuff that they might not want to retrieve. (Apple has rejected apps that request too much data from a cellular connection.) Or, at the very least, if you want to retrieve stuff up front, only retrieve stuff as you need it (e.g. in cellForRowAtIndexPath), which will retrieve the visible rows, rather than the hundreds of rows that might be in your text/plist/json file.
Frankly, we need a clearer articulation of what you're trying to do, and we might be able to help you with more concise counsel.

NSURLConnection sendAsynchronousRequest never free up the memory

I am working on an iOS app which dispatch quite a number of tasks to my serial queue. The task is to download images from my web server, save it to disk, and later displayed on UIImageView. However, [NSURLConnection sendAsynchrousRequest] will keep eating up more and more memory until iOS kill my process.
The downloader method looks like this:
// dispatch_queue_t is created once by: m_pRequestQueue = dispatch_queue_create( "mynamespace.app", DISPATCH_QUEUE_SERIAL);
- (void) downloadImageInBackgroundWithURL:(NSString*) szUrl {
__block typeof(self) bSelf = self;
__block typeof(m_pUrlRequestQueue) bpUrlRequestQueue = m_pRequestQueue;
dispatch_async( m_pRequestQueue, ^{
NSAutoreleasePool *pAutoreleasePool = [[NSAutoreleasePool alloc] init];
NSURLRequest *pRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:szUrl]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:URL_REQUEST_TIMEOUT];
[NSURLConnection sendAsynchronousRequest:pRequest queue:bpUrlRequestQueue completionHandler:^(NSURLResponse *pResponse, NSData *pData, NSError *pError) {
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
if ( pError != nil ) {
} else {
// convert image to png format
UIImage *pImg = [UIImage imageWithData:pData];
NSData *pDataPng = UIImagePNGRepresentation(pImg);
bool bSaved = [[NSFileManager defaultManager] createFileAtPath:szCacheFile contents:pDataPng attributes:nil];
}
__block typeof(pDataPng) bpDataPng = pDataPng;
__block typeof(pError) bpError = pError;
dispatch_sync( dispatch_get_main_queue(), ^ {
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
UIImage *pImage = [[UIImage alloc] initWithData:bpDataPng];
// display the image
[pImage release];
// NSLog( #"image retain count: %d", [pImage retainCount] ); // 0, bad access
[autoreleasepool drain];
});
}
[pPool drain];
}]; // end sendAsynchronousRequest
[pAutoreleasePool drain];
}); // end dispatch_async
} // end downloadImageInBackgroundWithURL
I am quite sure it is something inside [NSURLConnection sendAsynchronousRequest] as the profiler is showing that the function is the one eating up all the memory...
However, I am also not very sure about the dispatch_*** and block things, I've always used C and C++ code with pthread before, but after reading from Apple's documentation on migrating away from thread, I decided to give GCD a try, objective-c is so troublesome and I'm not sure how to release the NSData *pData and NSURLResponse *pResponse as it crash whenever I do it.
Please advice... really need help to learn and appreciate objective-c...
ADDITIONAL EDIT:
Thanks to #robhayward, I put the pImg and pDataPng outside as __block variable, use his RHCacheImageView way of downloading data ( NSData initWithContentOfURL )
Thanks as well to #JorisKluivers, the first UIImage can actually be reused to display as UIImageView recognized both jpg and png format, just my later processing requires png format and I am reading from the disk later just when required
I would firstly put it down to the image and data objects that you are creating:
UIImage *pImg = [UIImage imageWithData:pData];
NSData *pDataPng = UIImagePNGRepresentation(pImg);
Which might be hanging around too long, perhaps put them outside the block, as they are probably being created/released on different threads:
__block UIImage *pImg = nil;
__block NSData *pDataPng = nil;
[NSURLConnection sendAsynchronousRequest..
(Also consider using ARC if you can)
I have some code on Github that does a similar job without this issue, feel free to check it out:
https://github.com/robinhayward/RHCache/blob/master/RHCache/RHCache/Helpers/UIImageView/RHCacheImageView.m
First of all try simplifying your code. Things I did:
Remove the outer dispatch_async. This is not needed, your sendAsynchronousRequest is async already. This also removes the need another __block variable on the queue.
You create an image named pImg from the received pData, then convert that back to NSData of type png, and later create another image pImage from that again. Instead of converting over and over, just reuse the first image. You could even write the original pData to disk (unless you really want the png format on disk).
I didn't compile the code below myself, so it might contain a few mistakes. But it is a simpler version that might help solve the leak.
- (void) downloadImageInBackgroundWithURL:(NSString*)szUrl
{
__block typeof(self) bSelf = self;
NSAutoreleasePool *pAutoreleasePool = [[NSAutoreleasePool alloc] init];
NSURLRequest *pRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:szUrl]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:URL_REQUEST_TIMEOUT];
[NSURLConnection sendAsynchronousRequest:pRequest queue:m_pRequestQueue completionHandler:^(NSURLResponse *pResponse, NSData *pData, NSError *pError) {
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
if (pError) {
// TODO: handle error
return;
}
// convert image to png format
__block UIImage *pImg = [UIImage imageWithData:pData];
// possibly just write pData to disk
NSData *pDataPng = UIImagePNGRepresentation(pImg);
bool bSaved = [[NSFileManager defaultManager] createFileAtPath:szCacheFile contents:pDataPng attributes:nil];
dispatch_sync( dispatch_get_main_queue(), ^ {
// display the image in var pImg
});
}];
[pAutoreleasePool drain];
}

Errors autoreleasing objects just after their creation

I'm starting to look into IOS Development and I have some doubts about releasing objects for which I did not store a reference.. I gave a look at the question "Release an object without a pointer?" where it is suggested to send the autorelease message to the object immediately after its creation, and so I tried to do the same in the following piece of code:
int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data;
data = [NSURLConnection
sendSynchronousRequest: [[NSURLRequest requestWithURL:
[[NSURL URLWithString: #"http://www.google.it"]
autorelease]
] autorelease]
returningResponse: &response
error: &error];
[data writeToFile: #"/tmp/test.html"
atomically:NO];
[data release];
[pool drain];
return 0;
}
I could not try to execute the program in XCode yet, but I'm compiling under linux and the autorelease message sent to the NSURLRequest object causes a segmentation error (I think it is not caused by the message itself but by the pool drain that tries to release the object, due to the autorelease message). What's wrong with the autorelease message I've sent to the NSURLRequest object?
I think that if the reference doc for a class method like requestWithUrl says that it "Creates and returns a URL request" it means that I'm responsible to release the object when I've finished using it, am I wrong? I'd like to understand very well this memory management rules before going further with anything else.. I hope my questions are not too stupid ;-)
Uh, just one last question: should I release also the error and data objects returned by the synchrounous request?
Thank you in advance for any help!
+requestWithURL: (and other) methods already return autoreleased objects so you should not send one more autorelease to them.
Extra autoreleases in your code make object over-released later and make app crash.
Rule of thumb to know if you must release an object - release required only if you create object using method that contains 'alloc', 'new', 'copy' in its name. All standard APIs follow this rule and you should follow it when developing your own methods.
So corrected code will be:
int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data;
data = [NSURLConnection
sendSynchronousRequest: [NSURLRequest requestWithURL:
[NSURL URLWithString: #"http://www.google.it"]
returningResponse: &response
error: &error];
[data writeToFile: #"/tmp/test.html"
atomically:NO];
[pool drain];
return 0;
}
P.S. Neither data and error objects should be released for the above reasons.
Your code should look like this:
int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data;
data = [NSURLConnection
sendSynchronousRequest: [NSURLRequest requestWithURL:[NSURL URLWithString: #"http://www.google.it"]] returningResponse:&response error: &error];
[data writeToFile: #"/tmp/test.html"
atomically:NO];
[pool drain];
return 0;
}
You don't need to call autorelease to your objects created with that methods.\
You don't need to release data and error.
All methods that returns object using next notation NSClass *object = [NSClass classSomeMagicWords]; will return autoreleased object, which you should not release if you don't call retain.
You should remove the autoreleases. In the iOS/Mac OSX dev, with the Classes supplied by Apple it’s pretty much the rule that if you’re creating an object with a method that does not involve the word init, you’re given an already autoreleased object.
For example:
NSString *blaah = [[NSString alloc] init];
Would return an object that you need to release later.
NSURL *googlelink = [NSURL URLWithString: #"http://www.google.it"];
on the other hand will give you an autoreleased object and if you release it again, it will crash.
In iOS memory management, you only own objects you create by allocating memory for it or copying it (methods either starts with alloc or have "copy" in its name).
You only need to release/autorelease them if you own them. Methods such as requestWithURL or URLWithString returns already autoreleased objects.
Check this doc from Apple developer site for more information.

Resources