Errors autoreleasing objects just after their creation - ios

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.

Related

What might be happening if I release object, after releasing pool that object belongs to?

I am asking just logical question. Will object be released from memory if I release pool first and then release the object ? For example, here is my code snippet:
[self performSelectorInBackground:#selector(setImage) withObject:nil];
-(void)setImage
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSString *strUrl = #"--some URL--";
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:strUrl]];
UIImage *myimage = [[UIImage alloc] initWithData:imageData];
[pool release];
[imageData release];
}
Assume that the code snippet executes under non-ARC environment.
This will be fine as you never added the imageData object to the autorelease pool, so the pool won't release it. If you had added the imageData object to the autorelease pool like this:
[[[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:strUrl]] autorelease];
The extra release would be an overrelease and your app may crash.
It looks like you've leaked myImage as this is never released.
when you call [pool release]; immediate deallocation wont happen to the autorelease objects that are added to the pool. So when you explicitly call release your imageData's reference count would reduce by 1. When pool releases either application would crash with deallocated instance or message sent to nil object.

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];
}

NSURL Request? Registration and Login

Here is my code:
//login method
- (int) authenticateClient {
NSString *loginWeb = [NSString stringWithFormat:(#"http://192.168.118.1/login.php?uname=%#&pass=%#&submit=Log%%20In"), user, pass];
NSURL *login = [NSURL URLWithString:loginWeb];
NSData *loginData = [NSData dataWithContentsOfURL: login];
NSString *result = [[NSString alloc] initWithData:loginData encoding:NSUTF8StringEncoding];
NSMutableURLRequest *loginRequest = [[NSMutableURLRequest alloc] init];
[loginRequest setURL:login];
[loginRequest setTimeoutInterval:1];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:loginRequest delegate:self startImmediately:YES];
[connection start];
return [result integerValue];
}
My question is, when simulating having no network connection, my app freezes up and doesn't load or time out in the specified interval (1 second just to see if it works).
I read something about start immediately forces in to run on the current threat causing everything else to "pause" until the action is complete.
I have two questions:
1.) What is a better method to have the URL basically run in the background instead of pausing everything?
2.) How can I get a timeout method that actually works?
Much appreciation! Thanks!
Well, one second as timeout is really low. If you want to simulate different network conditions, you can use something like this. You don't need to this dance:
NSData *loginData = [NSData dataWithContentsOfURL: login];
NSString *result = [[NSString alloc] initWithData:loginData encoding:NSUTF8StringEncoding];
You don't need this as well, since the connection has started already:
[connection start];
Also from the documentation:
NSURLConnection’s delegate methods—defined by the
NSURLConnectionDelegate Protocol protocol—allow an object to receive
informational callbacks about the asynchronous load of a URL request.
Other delegate methods provide facilities that allow the delegate to
customize the process of performing an asynchronous URL load. These
delegate methods are called on the thread that started the
asynchronous load operation for the associated NSURLConnection object.
So for your questions:
Implement the NSURLConnection’s delegate
Check this & this.

Memory leak that appears to be in touchJSON

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];
}

Memory Leak Copying data From server

for (int i=0; i<[images count] ;i++) {
url=#"http://192.168.0.101/titan/titanimages/";
url=[url stringByAppendingString:[images objectAtIndex:i]];
//NSData *imageData=[[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
NSData *imageData=[NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
destinationPath=[documentsDirectory stringByAppendingString:#"/modelimages"];
destinationPath=[destinationPath stringByAppendingPathComponent:[images objectAtIndex:i]];
[imageData writeToFile:destinationPath atomically:YES];
value=value+divideValue;
printf("%f\n",value);
[NSThread detachNewThreadSelector:#selector(updateProgressBar)toTarget:self withObject:nil];
}
This code has a memory leak: it does not release memory of NSdata and after some time memory utilization of application reaches 61 MB. Can anyone help me get out of this?
Not 100% sure, but it probably has to do with the use of the "convenience constructor" with the NSData class in particular. When you call "dataWithContentsOfURL" you'll get back an NSData object that is automatically auto-released. However, your current NSAutoreleasePool might not be in a scope that will result in that memory being released until the application exits. You could try switching back to the alloc/init call you have commented out, and try manually releasing each NSData object inside the loop, to guarantee that the NSData memory is released for each instance of NSData created in the loop (after you've saved off the NSData to file).

Resources