Reputation: 1777
I have a few views which parse xml downloaded from the internet.
The instruments leaks tool tells me I have leaks when I release data members inside the dealloc method but not when i put the [objectname release];
inside viewDidDissapear
.
Is this a cardinal sin?
Coming from a c/c++ background I find obj-c memory management very confusing!
EDIT: Here is the code:
#import "SuggestedFriendList.h"
@implementation SuggestedFriendList
@synthesize maincell,nationalityimageview, subjectimageview, accommodationimageview;
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
currentElement = [[elementName copy] autorelease];
if ([elementName isEqualToString:@"shared"])
{
tshared = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"fullname"])
{
tfullname = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"nationality"])
{
tnationality = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"subject"])
{
tsubject = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"accommodation"])
{
taccommodation = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"memberid"])
{
tmemberid = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"count"])
{
tcount = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([currentElement isEqualToString:@"shared"])
{
[tshared appendString:string];
}
if ([currentElement isEqualToString:@"fullname"])
{
[tfullname appendString:string];
}
if ([currentElement isEqualToString:@"nationality"])
{
[tnationality appendString:string];
}
if ([currentElement isEqualToString:@"subject"])
{
[tsubject appendString:string];
}
if ([currentElement isEqualToString:@"accommodation"])
{
[taccommodation appendString:string];
}
if ([currentElement isEqualToString:@"memberid"])
{
[tmemberid appendString:string];
}
if ([currentElement isEqualToString:@"count"])
{
[tcount appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"shared"])
{
[lshared addObject:tshared];
[tshared release];
}
if ([elementName isEqualToString:@"fullname"])
{
[lfullname addObject:tfullname];
[tfullname release];
}
if ([elementName isEqualToString:@"nationality"])
{
[tnationality appendString:@".png"];
[lnationality addObject:tnationality];
[tnationality release];
}
if ([elementName isEqualToString:@"subject"])
{
[lsubject addObject:tsubject];
[tsubject release];
}
if ([elementName isEqualToString:@"accommodation"])
{
[laccommodation addObject:taccommodation];
[taccommodation release];
}
if ([elementName isEqualToString:@"memberid"])
{
[lmemberid addObject:tmemberid];
[tmemberid release];
}
if ([elementName isEqualToString:@"count"])
{
count = [[NSMutableString alloc] init];
count = tcount;
[tcount release];
}
}
-(void)fetchsuggestions
{
MyManager *sharedManager = [MyManager sharedManager];
suggestiondetailxml = [[NSMutableData alloc] init];
NSString *urlString = [NSString stringWithFormat:@"http://secreturl.com/a.php?username=%@&password=%@",sharedManager.user,sharedManager.passw];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}
-(void) connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
[suggestiondetailxml appendData:data];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)conn
{
NSString *xmlcheck = [[[NSString alloc] initWithData:suggestiondetailxml encoding:NSUTF8StringEncoding] autorelease];
NSLog(@"%@",xmlcheck);
lshared = [[NSMutableArray alloc] init];
lfullname = [[NSMutableArray alloc] init];
lnationality = [[NSMutableArray alloc] init];
lsubject = [[NSMutableArray alloc] init];
laccommodation = [[NSMutableArray alloc] init];
lmemberid = [[NSMutableArray alloc] init];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData: suggestiondetailxml];
[parser setDelegate:self];
[parser parse];
[parser release];
//[xmlcheck release]; //causes crash
[connection release];
connection = nil;
[suggestiondetailxml release];
[[self tableView] reloadData];
NSLog(@"%@",lmemberid);
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[[self navigationItem] setTitle:@"My Culture"];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self fetchsuggestions];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[lshared release];
[lfullname release];
[lnationality release];
[lsubject release];
[laccommodation release];
[lmemberid release];
//[count release];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [lfullname count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 93.0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell==nil)
{
[[NSBundle mainBundle] loadNibNamed:@"SuggestedFriendCell" owner:self options:nil];
cell = maincell;
//self.detailcell = nil;
}
UILabel *name;
name = (UILabel *)[cell viewWithTag:4];
name.text=[lfullname objectAtIndex:indexPath.row];
if(([[lshared objectAtIndex:indexPath.row]isEqualToString:@"all"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"nationalityandaccommodation"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"nationalityandsubject"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"nationality"]))
{
UIImageView *nationality;
nationality = (UIImageView *)[cell viewWithTag:1];
nationality.image=[UIImage imageNamed:[lnationality objectAtIndex:indexPath.row]];
}
if(([[lshared objectAtIndex:indexPath.row]isEqualToString:@"all"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"nationalityandaccommodation"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"subjectandaccommodation"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"accommodation"]))
{
UIImageView *accommodation;
accommodation = (UIImageView *)[cell viewWithTag:2];
accommodation.image=[UIImage imageNamed:@"accommodation.jpeg"];
}
if(([[lshared objectAtIndex:indexPath.row]isEqualToString:@"all"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"nationalityandsubject"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"subjectandaccommodation"])||([[lshared objectAtIndex:indexPath.row]isEqualToString:@"subject"]))
{
UIImageView *subject;
subject = (UIImageView *)[cell viewWithTag:3];
subject.image=[UIImage imageNamed:@"degree.jpg"];
}
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
MyManager *sharedManager = [MyManager sharedManager];
int row = indexPath.row;
sharedManager.interrogatedmemberid=[lmemberid objectAtIndex:row];
ViewFriendProfile *frienddetail_vc = [[[ViewFriendProfile alloc] init] autorelease];
[[self navigationController] pushViewController:frienddetail_vc animated:YES];
}
-(void)dealloc
{
[super dealloc];
}
@end
When I uncomment [tcount release] it causes crashes: why is this.
PS: I am SO sorry about the formatting: How can you copy and paste code so it appears in the code blocks?
Thanks
Upvotes: 1
Views: 366
Reputation: 9536
To understand what's going on you need to understand how viewWillAppear and viewWillDisappear work.
Both methods are invoked multiple times in a view controller's life cycle depending on whether you're pushing/popping a viewcontroller, or if you're displaying / dismissing a modal from a view controller.
For example:
Sounds like you're allocating an object in viewWillAppear, which is called multiple times, but deallocating it in the dealloc method, which is called only once, hence the memory leak.
Releasing your object in viewWillDisappear is balancing out the allocs in viewWillAppear hence there is no leak.
All I can say at this point is, unless you have a very good reason to allocate something in viewWillAppear (i.e., you know what you're doing), don't do it.
There's not much more I can tell you... posting more code for what you're trying to do might help you get a more elaborate answer :)
Hope this helped though.
EDIT after the code was posted.
First of all, do you mean 'when I uncomment [count release]'? Could you post the console message for that crash? It's crashing because parserDidEndElement is where it's allocated, but it is getting released in viewWillDisappear. If these don't balance out, you'll get an EXC_BAD_ACCESS
Coming to your memory situation. I see now why releasing in viewWillDisappear fixes the leaks. You're calling fetchsuggestions
on viewWillAppear which in turn creates and fires off the URL request. This will lead to connectionDidFinishLoading
being called on a successful connection. Here is where you're allocating the various ivars. Since I mentioned earlier that viewWillAppear is called multiple times, this means that you're firing off the connection multiple times which leads to connectionDidFinishLoading
being called multiple times.
That is why releasing in viewWillDisappear
fixes the leaks because for every allocate on a viewWillAppear
, you balance it by releasing in viewWillDisappear
.
What you missed though is that your program will crash when there's no network available. This is because connectionDidFinishLoading
will not get called when there's no network. Now, when you try releasing in viewWillDisappear
, you will get an EXC_BAD_ACCESS as you're trying to release previously deallocated instances which were never allocated again in viewWillAppear
.
I don't think I see a need to call fetchsuggestions
multiple times on viewWillAppear, unless you're doing it intentionally.
This is the most glaring thing I see right now... the issues could be (and probably are) multiple, but how about we start off by restructuring the code keeping in mind that multiple unbalanced allocs and releases inside viewWillAppear/Disappear might not be a good idea...? :)
Upvotes: 8
Reputation: 9132
It's kind of strange to release anything in viewWillDisappear, but if you allocate it in viewWillAppear, that's the right thing to do. I think you should be allocating your objects in viewDidLoad, and releasing them in viewDidUnload. In some cases that would avoid unnecessarily recreating them.
You should also release retained ivars in dealloc. Just make sure that when you release them in other methods, you set them to nil, so if they get released again, it's just sending release to nil, which is a no-op.
Upvotes: 1
Reputation: 5085
There is a possibilty that you are not completely releasing the view and dealloc is never being called.
Upvotes: 0
Reputation: 11607
One tool that I find helpful in finding potential memory leaks is Build > Analyse in Xcode.
It tells you which line did the allocation and where might it leak later down the code. It also tells you where you have made incorrect [object release] decrement count.
Upvotes: 0