views:

661

answers:

1

Dear friends, I'm about to make a fool out of myself, but I've noticed many friendly and patient people around here, so I just give it a try:

I'm developing an iPhone app, that contains a database of car reviews. I want the user to be able to share the reviews by email. So, when he/she finds an interesting car in the app, he/she would hit a button, and the app composes an email through the iPhone's Mail.app.

Now. I'm a newbie, and I have to admit that I'm not too familiar with memory management on the iPhone. The code that I wrote, this specific mail method, exits the application with the scary "Program received signal: “EXC_BAD_ACCESS”" message. A bit of Googling suggest that this is the result of bad memory management.

With my little understanding of this matter, I started explicitly initializing and afterwards releasing all temporary variables, like a madman. Nevertheless, the “EXC_BAD_ACCESS” keeps on showing up.

Interesting point here: as soon as I kill my app, the constructed URL still triggers Mail.app, and happily creates the email for me.

Please consider the following sample code, and shoot me.

- (IBAction) sendCartoFriend
{
    CarAppDelegate *appDelegate = (CarAppDelegate *)[[UIApplication sharedApplication] delegate];

    //Read the html template
    NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
    NSString *emailFile = [resourcePath stringByAppendingPathComponent:@"MailDummy.html"];
    NSMutableString *eMailRaw = [[[NSMutableString alloc] initWithContentsOfFile:emailFile]autorelease];

    //set the variables
    NSString *carNamePlaceholder = [[NSString alloc] initWithString:@"CarTitle"];
    NSString *carName = [[NSString alloc] initWithString:car.shortname];
    [eMailRaw replaceOccurrencesOfString:carNamePlaceholder withString:carName options:NSCaseInsensitiveSearch range:NSMakeRange(0, [eMailRaw length])];
    [carNamePlaceholder release];
    [carName release];

    NSString *carReviewPlaceholder = [[NSString alloc] initWithString:@"CarReview"];
    NSString *carReview = [[NSString alloc] initWithString:car.review];
    [eMailRaw replaceOccurrencesOfString:carReviewPlaceholder withString:carReview options:NSCaseInsensitiveSearch range:NSMakeRange(0, [eMailRaw length])];
    [carReviewPlaceholder release];
    [carReview release];

    //there are 5 more of these find/replace actions. the "CarReview" though is the biggest. It might contain several hundred of characters.

    //compose the message
    NSString *eMailSubject = @"Nice little car!";
    NSString *encodedSubject = [[NSString alloc] initWithString:[eMailSubject stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; 
    NSString *eMailBody = eMailRaw;
    NSLog(eMailBody);
    NSString *encodedBody = [[NSString alloc] initWithString:[eMailBody stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];


    NSString *urlString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"mailto:?subject=%@&body=%@", encodedSubject, encodedBody]];
    NSURL *url = [[NSURL alloc] initWithString:urlString];

    [urlString release];
    [encodedBody release];
    [encodedSubject release];
    [eMailRaw release];

    [[UIApplication sharedApplication] openURL:url];

    [url release];
    }
+3  A: 

Hmm.. at first glance:

you're releasing eMailRow even though it's set to autorelease.

Could this be the problem?

rein
Actually, I commented out the [eMailRaw release]; and ever since, with a few random tests, the problem did not occur! Thank you so much.Still, I feel a bit uncertain about the init/release thing. Do I really have to frantically clean up stuff like this? It seams to be a bit overdone.I read something about Lazy Loading. Is that something I could/should use here? What would be the advantage, and how would Lazy Loading translate in my code?I guess that is another question, but any help is appreciated.
The memory management rules are quite simple and are absolutely vital to know - otherwise you're going to be tearing your hair out frequently. Quite simply, anything you alloc, copy or retain needs a corresponding release or autorelease. Too many (or too few) releases on an object gives you a EXC_BAD_ACCESS.Lazy loading does not change those fundamental rules.
rein
If you init something, as a rule you must release it. You can generally use convenience constructors for temporary variables so that you're not responsible for managing the memory. Understanding Cocoa memory management is very important, though. Apple's guide is a must-read: < http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html >. There's definitely a learning curve, but once you get over the hump, it's surprisingly easy.
Chuck
That's what I kind of hoped for. So far, I managed to avoid the memory management stuff. Can you say that, except for the superfluous 'release', this code is agreeable, in terms of memory management? In that case I will have to review all of my code, because so far, I haven't been that precise with it. Quite likely, looking at the rest of my code, the [eMailRaw release] is the one single culprit. Most of the times I would just do something like: NSString *carNamePlaceholder = @"CarTitle"without any release, and nothing would go wrong.Is that sloppy coding?
No that's not sloppy coding. You didn't alloc, copy or retain it so you don't need a release. In that case the string will be released at the end of the current run loop and you don't need to worry about it. With regards to finding leaks in your code (or if you're worried about it), simply run your application through the Leaks application. You might need to Google a bit to find out how it works exactly but that profiler will tell you if you are losing memory anywhere in your application.
rein
Thank you Rein. In this case, the 'loop' would just be the method "sendCartoFriend" itself? I could have made my life a lot easier by just not allocating anything?