dealloc vs ViewDidDissapear для управления памятью - PullRequest
1 голос
/ 02 ноября 2011

У меня есть несколько просмотров, которые разбирают xml, скачанный из интернета.

Инструмент утечки инструментов говорит мне, что у меня есть утечки, когда я освобождаю элементы данных внутри метода dealloc, но не когда я помещаю [objectname release]; внутрь viewDidDissapear.

Это кардинальный грех?

Исходя из опыта работы с c / c ++, я считаю, что управление памятью в obj-c очень запутанно!

РЕДАКТИРОВАТЬ: Вот код:

#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

Когда я раскомментирую [tcount release], это вызывает сбои: почему это так.

PS: мне так жаль по поводу форматирования: как вы можете скопировать и вставить код, чтобы он появлялся в блоках кода?

Спасибо

Ответы [ 4 ]

8 голосов
/ 02 ноября 2011

Чтобы понять, что происходит, вам нужно понять, как работают viewWillAppear и viewWillDisappear.

Оба метода вызываются несколько раз в жизненном цикле контроллера представления, в зависимости от того, нажимаете ли вы / вынимаете контроллер вида, или есливы отображаете / отклоняете модал от контроллера представления.

Например:

  1. Push viewController A: viewDidLoad и viewWillAppear вызывается для viewController A
  2. Push viewController B из viewController A: viewWillDisappear вызывается для viewController A и viewDidLoviewWillAppear вызывается для viewController B
  3. Вызывает viewController B для возврата к viewController A: viewWillDisappear вызывается для viewController B, viewDidUnload вызывается для viewController B, dealloc вызывается для viewController B, viewWill *ppear вызывается для viewController B 1012 * *22 * вызывается для viewController 1013Отображение модального из viewController A: viewWillDisappear вызывается для viewController A
  4. Отключить модальное: viewWillAppear, вызываемое для viewController A
  5. Всплывающее представление viewController A: viewWillDisappear вызывается для viewController A, viewDidController вызывается для viewDidControlВызывается на viewController A

Звучит так, как будто вы выделяете объект в viewWillAppear, который вызывается несколько раз, но освобождает его в методе deallocd, который вызывается только один раз, следовательно, утечка памяти .

Освобождение вашего объекта в viewWillDisappear уравновешивает ресурсы в viewWillAppear, следовательно, утечки нет.

Все, что я могу сказатьна данный момент, если у вас нет очень веской причины выделять что-то в viewWillAppear (то есть вы знаете, что делаете), не делайте этого.

Больше я не могу вам сказать... размещение большего количества кода для того, что вы пытаетесь сделать, может помочь вам получить более сложный ответ:)

Надеюсь, что это помогло.


РЕДАКТИРОВАТЬ после того, как код был опубликован.

Прежде всего, вы имеете в виду «когда я раскомментирую [ count release]»?Не могли бы вы опубликовать консольное сообщение для этого сбоя?Это происходит сбой, потому что parserDidEndElement находится там, где он размещен, но он становится доступным в viewWillDisappear.Если они не сбалансированы, вы получите EXC_BAD_ACCESS

в зависимости от ситуации с памятью.Теперь я вижу, почему выпуск в viewWillDisappear исправляет утечки.Вы вызываете fetchsuggestions для viewWillAppear, который, в свою очередь, создает и запускает запрос URL.Это приведет к connectionDidFinishLoading вызову при успешном соединении.Здесь вы размещаете различные ивары.Поскольку я упоминал ранее, что viewWillAppear вызывается несколько раз, это означает, что вы запускаете соединение несколько раз, что приводит к тому, что connectionDidFinishLoading вызывается несколько раз.

Именно поэтому выпуск в viewWillDisappear исправляетутечки, потому что для каждого выделения в viewWillAppear вы балансируете его, выпуская в viewWillDisappear.

Что вы пропустили, хотя , так это то, что ваша программа потерпит крах, когда нет доступной сети.Это потому, что connectionDidFinishLoading не будет вызван, когда нет сети.Теперь, когда вы попытаетесь выпустить в viewWillDisappear, вы получите EXC_BAD_ACCESS, когда вы пытаетесь освободить ранее освобожденные экземпляры, которые никогда не были распределены снова в viewWillAppear.

Я не думаю, что вижуМне нужно вызывать fetchsuggestions несколько раз в viewWillAppear, если вы не делаете это намеренно.

Это самая яркая вещь, которую я вижу сейчас ... проблемы могут быть (и, вероятно, являются) множественными, нокак насчет того, чтобы начать с реструктуризации кода, помня о том, что несколько несбалансированных распределений и выпусков внутри viewWillAppear / Disappear может не быть хорошей идеей ...?:)

1 голос
/ 02 ноября 2011

Странно выпускать что-либо в viewWillDisappear, но если вы выделите это в viewWillAppear, это будет правильно. Я думаю, что вы должны размещать свои объекты в viewDidLoad и освобождать их в viewDidUnload. В некоторых случаях это позволит избежать излишнего воссоздания их.

Вы должны также выпустить оставшиеся ивы в dealloc. Просто убедитесь, что когда вы выпускаете их другими методами, вы устанавливаете их на nil, поэтому, если они выпускаются снова, это просто отправляет релиз на nil, который не используется.

0 голосов
/ 02 ноября 2011

Существует вероятность того, что вы не полностью освобождаете представление, и dealloc никогда не вызывается.

0 голосов
/ 02 ноября 2011

Один инструмент, который я нахожу полезным в поиске потенциальных утечек памяти, - это Build> Analyze в Xcode.

Он сообщает, какая строка выполнила выделение и где она может просочиться позже в коде.Также сообщается, где вы сделали неправильный счетчик декрементов [выпуска объекта].

...