2012-06-26 10 views
5

Tengo un UIViewController que se presenta de forma modal. Cuando veo el instrumento de asignaciones de memoria, el uso de la memoria aumenta cuando se presenta la vista, pero cuando se sale, la memoria no se libera. Si sigo abriendo y cerrando la vista, la memoria sigue aumentando. ¡Los instrumentos no informan una pérdida de memoria! ¿Qué podría estar causando esto? El código View Controller está debajo (me salté el código didSelectRow). Dealloc siempre se llama.Posible pérdida de memoria en UIViewController con UITableView

EDITAR - Estoy usando ARC

.h

#import <UIKit/UIKit.h> 
@class OutlineTextUILabel; 

@interface StoreViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> { 

    int starCount; 
    NSMutableArray *_singleUseArray; 
    NSMutableArray *_fullUseArray; 

} 

@property (weak, nonatomic) IBOutlet UITableView *tableView; 
@property (weak, nonatomic) IBOutlet OutlineTextUILabel *starCountLbl; 
- (IBAction)exitBtnPressed:(id)sender; 

.m

#import "StoreViewController.h" 
#import "NSUserDefaults+MPSecureUserDefaults.h" 
#import "PowerUpCell.h" 
#import "OutlineTextUILabel.h" 
#import "PowerUpSingleton.h" 
#import "PowerUp.h" 

#define kPrefsNumberOfStars    @"numberOfStars" 

@interface StoreViewController() 

@end 

@implementation StoreViewController 
@synthesize tableView = _tableView; 
@synthesize starCountLbl; 

#pragma mark View Methods 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // Display star count 
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; 
    BOOL valid = NO; 
    starCount = [prefs secureIntegerForKey:kPrefsNumberOfStars valid:&valid]; 
    if (!valid) { 
     NSLog(@"Stars Tampered With!"); 
     self.starCountLbl.text = @"Err"; 
    } else { 
     self.starCountLbl.text = [NSString stringWithFormat:@"%d",starCount]; 
    } 

    // Tableview setup 
    CGRect frame2 = CGRectMake(0, 0, 320, 40); 
    UIView *footer = [[UIView alloc] initWithFrame:frame2]; 
    footer.backgroundColor = [UIColor clearColor]; 
    self.tableView.tableFooterView = footer; 
    self.tableView.opaque = NO; 
    self.tableView.backgroundView = nil; 
} 

- (void)viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:YES]; 

    if (![[PowerUpSingleton sharedList] refreshArray]) { 
     NSLog(@"Error, %s",__FUNCTION__); 
    } else { 
     [self performSelectorOnMainThread:@selector(workOutSingleUseToDisplay) withObject:nil waitUntilDone:YES]; 
     [self performSelectorOnMainThread:@selector(workOutFullUseToDisplay) withObject:nil waitUntilDone:YES]; 
     [self.tableView reloadData]; 
    } 
} 

- (void)workOutSingleUseToDisplay 
{ 
    _singleUseArray = [[NSMutableArray alloc] init]; 
    for (PowerUp *pu in [[PowerUpSingleton sharedList] sharedArray]) { 
     if (!pu.fullUnlock) { 
      [_singleUseArray addObject:pu]; 
     } 
    } 
} 

- (void)workOutFullUseToDisplay 
{ 
    _fullUseArray = [[NSMutableArray alloc] init]; 
    for (PowerUp *pu in [[PowerUpSingleton sharedList] sharedArray]) { 
     if (pu.prefFullName != nil) { 
      [_fullUseArray addObject:pu]; 
     } 
    } 

} 

- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{ 
    return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown); 
} 

- (void)viewDidUnload { 
    [self setTableView:nil]; 
    [self setStarCountLbl:nil]; 
    [super viewDidUnload]; 
} 

#pragma mark TableView Setup Methods 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return 2; 
} 

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
{ 
    if (section == 0) { 
     return @"Single Use"; 
    } else if (section == 1) { 
     return @"Use forever"; 
    } 

    return nil; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    if (section == 0) { 
     return [_singleUseArray count]; 
    } else if (section == 1) { 
     return [_fullUseArray count]; 
    } 

    return 0; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSString *cellIdentifier; 
    if (indexPath.section == 0) { 
     cellIdentifier = @"powerUpCellSingleUse"; 
    } else if (indexPath.section == 1) { 
     cellIdentifier = @"powerUpCell"; 
    } 

    PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 
    if (cell == nil) { 
     cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 
    } 

    if (indexPath.section == 0) { 
     PowerUp *tmpPU = [_singleUseArray objectAtIndex:indexPath.row]; 
     cell.descriptionLbl.text = tmpPU.displayName; 
     int cost = tmpPU.costSingle; 
     cell.costLbl.text = [NSString stringWithFormat:@"%d",cost]; 
     if (cost > starCount) { 
      cell.costLbl.textColor = [UIColor redColor]; 
     } else { 
      cell.costLbl.textColor = [UIColor blueColor]; 
     } 
     int howMany = tmpPU.numberOwned; 
     cell.howManyLbl.text = [NSString stringWithFormat:@"%d",howMany]; 

    } else if (indexPath.section == 1) { 
     PowerUp *tmpPU = [_fullUseArray objectAtIndex:indexPath.row]; 
     cell.descriptionLbl.text = tmpPU.displayName; 
     int cost = tmpPU.costFull; 
     cell.costLbl.text = [NSString stringWithFormat:@"%d",cost]; 
     if (cost > starCount) { 
      cell.costLbl.textColor = [UIColor redColor]; 
     } else { 
      cell.costLbl.textColor = [UIColor blueColor]; 
     } 
     if (tmpPU.fullUnlock) { 
      cell.costLbl.textColor = [UIColor greenColor]; 
      cell.costLbl.text = @"---"; 
     } 
    } 

    return cell; 
} 

#pragma mark - 

- (IBAction)exitBtnPressed:(id)sender 
{ 
    [self dismissModalViewControllerAnimated:YES]; 
} 

- (void)dealloc 
{ 
    NSLog(@"%s",__FUNCTION__); 
    self.tableView = nil; 
    self.starCountLbl = nil; 
} 

@end 

EDITAR ------------- Algo no parece estar en lo cierto. He agregado un NSLog a la asignación de celda, y nunca se llama, ¡aunque las células se hayan creado!

PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 
    if (cell == nil) { 
     NSLog(@"new cell"); 
     cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 
    } 

EDITAR 1 ª de julio de ------ he añadido un controlador de navegación y ahora utilizan manual en lugar de modal y este problema es todavía aquí. He tomado disparos de montón con Instruments moviéndome hacia atrás y hacia adelante entre las vistas varias veces y parece que las celdas todavía están dando vueltas, ya que esta captura de pantalla muestra el reconocedor de gestos todavía alrededor de una carga previa de la vista. screen shot

+0

Vista interiorDesarrollarás performOnMainThread. Esto no es necesario, viewWillAppear ocurre en el hilo principal. –

+0

Solo usé este método, así que puedo configurar waitUntilDone: YES, así que ahora las matrices se han llenado antes de dibujar la tabla. – Darren

+0

Prueba esto: [self workOutFullUseToDisplay]. ¿Te das cuenta de que Objective-C es secuencial, verdad? –

Respuesta

3

Es debido a que utilizó sus IBOutlets como weak, en lugar de utilizar strong.

De hecho, creo que esto es un defecto en el entorno XCode, ya que debería advertirte de este tipo de comportamiento.

Como una práctica recomendada, sugeriría dejar que XCode genere IBOutlets arrastrando las vistas al código en el Interface Builder, para evitar tales trampas molestas.

+0

Sí, dije esto por encima. Una explicación de por qué sería bueno. Hace un tiempo leí para usar enlaces débiles para IBOutlets. Dejo que XCode genere los IBOutlets, pero en el emergente tiene una opción de Fuerte o Débil. Además, pensé que fuerte tendría más posibilidades de dejar algo atrás que débil. Pensé que un objeto de referencia débil sería eliminado cuando se elimine la vista. – Darren

+1

Realmente depende. Debería usar enlaces fuertes para IBOutlets que son propiedad de FileOwner, y enlaces débiles para IBOutlets que son utilizados por subvistas. https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html # // apple_ref/doc/uid/10000051i-CH4-SW6 Tenga en cuenta las partes que se relacionan con iOS (y no con OS X) – Gilbert

+2

Creo que, en su caso específico, las celdas UITableView fueron asignadas con un fuerte enlace, y hace referencia a la tabla vista (superview). Mientras que en el otro extremo, UITableview (que es débil), también hacía referencia a las celdas, lo que no permitía que ninguna de ellas tratara – Gilbert

0

[EDIT]

En el método de viewWillAppear, que han impreso para ver la frecuencia con que se mueve a través de su cláusula else. Para mí, parece que llamas a tus métodos workOutSingleUseToDisplay y workOutFullUseToDisplay. Cada vez que los llame, está asignando _singleUseArray y _fullUseArray. Solo porque te muevas dentro y fuera de una vista, no significa que llame al dealloc, o que libere automáticamente tus arrays actuales. Lo que creo que está viendo es que cuando se sale de su vista, no libera esas dos matrices, pero intenta reasignarlas.

[ORIGINAL] Bueno, en su viewDidLoad, usted realiza una asignación. En su dealloc, no veo una [publicación de pie de página]. ¡Esta puede ser tu fuga! Tampoco veo la liberación de sus matrices _singleUseArray o _fullUseArray

+0

Estoy usando ARC, por lo que no puedo liberarlo manualmente. Intenté agregar _singleUseArray = nil; _fullUseArray = nil; pie de página = nil; al dealloc pero sin cambio. – Darren

+1

Deberías haber hablado sobre ARC en tu publicación inicial, porque creo que es determinante para la respuesta. – Martin

+0

Perdón por eso. Lo agregaré a la pregunta. He movido las 2 asignaciones de matriz a viewDidLoad, por lo que definitivamente solo se llamarán una vez, pero los patrones de memoria siguen siendo los mismos. Se está llamando a los controladores de vista dealloc en cada cierre. ¿Alguna otra idea? – Darren

0

No estoy seguro si me dieron el anwer, pero hay algo extraño en el código:

que está utilizando propiedades débiles:

@property (weak, nonatomic) IBOutlet UITableView *tableView; 
@property (weak, nonatomic) IBOutlet OutlineTextUILabel *starCountLbl; 

Pero de acuerdo con the doc (búsqueda "débil"), weak propoerty es bastante similar a assign.

En dealloc usted, usted tiene

self.tableView = nil; 
self.starCountLbl = nil; 

Estoy bastante seguro de que el colocador generada de estas propiedades no libera en absoluto!

Pero si declara sus propiedades como:

@property (nonatomic, retain) IBOutlet UITableView *tableView; 
@property (nonatomic, retain) IBOutlet OutlineTextUILabel *starCountLbl; 

la incubadora generada sería como

(void)setTableView(UITableView *)newTableView { 
    [tableView release]; 
    if(newTableView != nil) 
     tableView = [newTableView retain]; 
} 

Y sus propiedades sería puesto en libertad.

+0

De hecho, solo agregué el self.tableView = nil y self.starCountLbl = nil para dealloc mientras trato de encontrar lo que no se está liberando. En realidad, no es necesario porque de todos modos son referencias débiles y se liberan cuando se lanza el controlador de vista. – Darren

0

Al menos, use el instrumento Leaks para controlar fugas de memoria. El instrumento Allocations en realidad no mostrará las pérdidas de memoria. Si ejecuta Analyze, verá las líneas que potencialmente están causando las filtraciones.

Este es su código:

PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 
if (cell == nil) { 
    NSLog(@"new cell"); 
    cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 
} 

Ves, cell no va a ser nil ... Esto se afirma en la documentación del API para dequeueReusableCellWithIdentifier::

Valor de retorno

Un objeto UITableViewCell con el identificador asociado o nil si tal objeto no existe en la cola de celda reutilizable.

De todos modos, si hay fugas, tal vez están en gran medida causadas por:

_singleUseArray = [[NSMutableArray alloc] init]; 

y

_fullUseArray = [[NSMutableArray alloc] init]; 

Cuando usted declaró

NSMutableArray *_singleUseArray; 
NSMutableArray *_fullUseArray; 

Creo que, por Por defecto ambos fueron asignados con un calificativo __strong ier. No estoy muy seguro, pero esta podría ser la verdadera causa del problema. ¿Qué hay de declarar esto en su lugar?

NSMutableArray * __weak _singleUseArray; 
NSMutableArray * __weak _fullUseArray; 

Además, antes de declarar

_singleUseArray = [[NSMutableArray alloc] init]; 

y

_fullUseArray = [[NSMutableArray alloc] init]; 

¿qué hay que asignar primero en nil para eliminar la referencia anterior?

_singleUseArray = nil; 
_singleUseArray = [[NSMutableArray alloc] init]; 

y

_fulUseArray = nil; 
_fullUseArray = [[NSMutableArray alloc] init]; 
+0

Utilicé la herramienta de fuga y no mostró una fuga aquí, aunque la memoria aumenta cada vez que se carga la vista. – Darren

+0

Tomar el array init completamente todavía tiene el problema :-( – Darren

+0

Me aparece 1 pérdida de memoria, pero no creo que esté relacionado. Dice frame responsable = [NSURL (NSURL) ruta] y cuando lo hago clic en él parece que tiene algo que ver con la carga del Storyboard. – Darren

2

Parece que ya has encontrado algunas formas de evitar esto, pero por si acaso esto ayuda:

1) Asegúrese de que usted no ha conseguido Zombies encendido mientras que va a depurar, ya que esto hace que los objetos quédese quieto después de pensar que deberían ser desasignados (Editar esquema -> Ejecutar -> Diagnóstico).

2) ¿Está utilizando ARC y entonces asumo storyboards o al menos prototipo de células UITableView en su storyboard/NIB? Si es así, entonces la razón por la que nunca se llama a su NSLog() a continuación es porque la llamada dequeueReusableCellWithIdentifier sabe para crear celdas a partir de estas celdas de prototipo a través del identificador de celda definido. Bastante práctico.

PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 
    if (cell == nil) { 
     NSLog(@"new cell"); 
     cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 
    } 

usted tiene que confiar en la UITableView para gestionar esta caché de UITableViewCells, y liberar de manera apropiada. Por lo tanto, es posible que simplemente estén dando vueltas porque su UITableView no se está lanzando (aunque creo que está diciendo que sí).

+0

¡Me ayudó! @ChrisH gracias por publicar esto. En realidad, tu primer consejo me ayudó a rastrear un comportamiento inexplicable. Publicaciones como esta hacen que Stack Overflow sea aún mejor. – scrrr

+0

¿puedes compartir cuál fue tu problema? Creo que podría tener algo similar en mi aplicación. – Piotr