5

Ho un UIViewController presentato in modo modale. Quando guardo lo strumento di allocazioni di memoria, l'utilizzo della memoria aumenta quando viene presentata la vista, ma quando viene interrotta la memoria non viene rilasciata. Se continuo ad aprire e chiudere la vista, la memoria continua ad aumentare. Gli strumenti non riportano una perdita di memoria! Cosa potrebbe causare questo? Il codice del controller di visualizzazione è sotto (ho saltato il codice didSelectRow). Dealloc viene sempre chiamato.Possibile perdita di memoria in UIViewController con UITableView

EDIT - sto 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 

EDIT ------------- Qualcosa non sembra avere ragione. Ho aggiunto un NSLog all'assegnazione delle celle e non viene mai chiamato, anche se le celle sono state create!

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

EDIT 1 luglio ------ Ho aggiunto un controller di navigazione e ora utilizzare spinta invece di modale e questo problema è ancora qui. Ho scattato degli heap con gli strumenti spostandomi indietro e avanti tra le visualizzazioni un paio di volte e sembra che le celle siano ancora in agguato, poiché questo screenshot mostra ancora il riconoscimento dei gesti da un precedente caricamento della vista. screen shot

+0

All'interno viewWillAppear you performOnMainThread. Non è necessario, viewWillAppear si verifica sul thread principale. –

+0

Ho usato questo metodo solo per impostare waitUntilDone: YES, quindi ora gli array sono stati riempiti prima di disegnare la tabella. – Darren

+0

Prova solo questo: [self workOutFullUseToDisplay]. Ti rendi conto che Objective-C è sequenziale, giusto? –

risposta

3

È perché hai usato il tuo IBOutlets come weak, invece di utilizzare strong.

Credo che questo sia un difetto nell'ambiente XCode, in quanto dovrebbe avvisare di questo tipo di comportamento.

Come best practice, suggerirei di consentire a XCode di generare gli IBOutlet trascinando le viste sul codice in Interface Builder, per evitare tali fastidiose insidie.

+0

Sì, l'ho già detto sopra.Una spiegazione del perché sarebbe buono.Io ho letto qualche tempo fa per usare i link deboli per IBOutlets.Io do a XCode di generare gli IBOutlet ma nel popup hai una scelta di Strong o Weak. Inoltre, ho pensato che il forte avrebbe più possibilità di lasciare qualcosa dietro che debole! Ho pensato che un oggetto di riferimento debole sarebbe stato rimosso quando la vista è stata rimossa – Darren

+1

In realtà dipende. È necessario utilizzare collegamenti sicuri per IBOutlet di proprietà di FileOwner e collegamenti deboli per IBOutlet utilizzati dalle visualizzazioni secondarie. https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html # // apple_ref/doc/uid/10000051i-CH4-SW6 Si prega di prendere nota delle parti relative a iOS (e non OS X) – Gilbert

+2

Credo che nel tuo caso specifico, le celle UITableView siano state allocate con un forte link, e fa riferimento al tableview (superview). Mentre dall'altra parte, UITableview (che è debole), faceva anche riferimento alle celle - che non permettevano a nessuno di loro di dealloc – Gilbert

0

[EDIT]

nel metodo viewWillAppear, hai stampato fuori per vedere quanto spesso ci si sposta attraverso la vostra clausola altro. A me sembra che tu chiami i tuoi metodi workOutSingleUseToDisplay e workOutFullUseToDisplay. Ogni volta che li chiami, stai allocando _singleUseArray e _fullUseArray. Solo perché ti muovi dentro e fuori da una vista, non significa che chiama il dealloc, o che rilascerà automaticamente i tuoi attuali array. Quello che penso tu stia vedendo è che quando ti allontani dalla tua vista, non rilascia questi due array, ma prova a riallocarli.

[ORIGINALE] Bene, nel tuo viewDidLoad, si esegue un alloc. Nel tuo dealloc, non vedo un [footer release]. Questa potrebbe essere la tua perdita !!! Né vedo rilascio degli array _singleUseArray o _fullUseArray

+0

Sto usando ARC quindi non posso rilasciare manualmente. Ho provato ad aggiungere _singleUseArray = nil; _fullUseArray = nil; footer = zero; al dealloc ma nessun cambiamento. – Darren

+1

Dovresti avere informazioni su ARC nel tuo post iniziale, perché penso che stia determinando la risposta. – Martin

+0

Mi dispiace per quello. Lo aggiungerò alla domanda. Ho spostato l'allocazione di 2 array a viewDidLoad in modo che vengano chiamati solo una volta, ma i pattern di memoria sono sempre gli stessi. Il controllore della vista dealloc viene richiamato ad ogni chiusura. Qualche altra idea? – Darren

0

non sono sicuro se ho ottenuto l'Anwer, ma c'è qualcosa di strano nel codice:

si sta usando le proprietà deboli:

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

Ma secondo the doc (ricerca "debole"), weak propoerty è abbastanza simile a assign.

In te dealloc, avete

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

Sono abbastanza sicuro che il setter generato di queste proprietà non esime affatto!

Ma se si dichiara come le proprietà:

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

il setter generato sarebbe come

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

E le proprietà sarebbero stati rilasciati.

+0

In realtà ho solo aggiunto self.tableView = nil e self.starCountLbl = nil a dealloc mentre cercavo di trovare cosa non rilasciare. Non è effettivamente necessario perché sono comunque riferimenti deboli e rilasciano quando viene rilasciato il controller di visualizzazione. – Darren

0

Almeno, utilizzare lo strumento Perdite per monitorare le perdite di memoria. Lo strumento Allocations non mostrerà effettivamente le perdite di memoria. Se esegui Analizza, vedrai le linee che potenzialmente causano le perdite.

Questo è il tuo codice:

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

Vedete, cell non sta per essere nil ... Ciò viene affermato nella documentazione delle API per dequeueReusableCellWithIdentifier::

Return Value

Un oggetto UITableViewCell con l'identificativo associato o zero se non esiste alcun oggetto di questo tipo nella coda delle celle riutilizzabili.

In ogni caso, se ci sono fughe di notizie, forse sono molto causati da:

_singleUseArray = [[NSMutableArray alloc] init]; 

e

_fullUseArray = [[NSMutableArray alloc] init]; 

Quando dichiarato

NSMutableArray *_singleUseArray; 
NSMutableArray *_fullUseArray; 

credo, da entrambi erano assegnati con un qualif __strong ier. Non sono sicuro, ma questa potrebbe essere la vera causa del problema. Che ne dici invece di dichiararlo?

NSMutableArray * __weak _singleUseArray; 
NSMutableArray * __weak _fullUseArray; 

Inoltre, prima di dichiarare

_singleUseArray = [[NSMutableArray alloc] init]; 

e

_fullUseArray = [[NSMutableArray alloc] init]; 

come su assegnandolo primo a nil per rimuovere il riferimento precedente?

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

e

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

Ho usato lo strumento di perdita e non ha mostrato una perdita qui, anche se la memoria aumenta ogni volta che viene caricata la vista. – Darren

+0

L'avvio completo dell'array ha ancora il problema :-( – Darren

+0

Viene visualizzata una perdita di memoria, ma non penso che sia correlata. Dice frame responsabile = [percorso NSURL (NSURL)] e quando faccio clic su esso sembra che abbia qualcosa a che fare con il caricamento dello Storyboard. – Darren

2

Sembra che tu abbia già trovato alcuni modi per aggirare questo, ma solo nel caso che questo aiuta:

1) Assicurarsi che non hai Zombies acceso mentre si sta debug, come questo fa sì che gli oggetti rimanete indietro dopo aver pensato che dovrebbero essere dealloc-ed (Modifica schema -> Esegui -> Diagnostica).

2) Stai utilizzando ARC e quindi presumo storyboard o almeno prototipo di celle UITableView nello storyboard/NIB? In tal caso, il motivo per cui NSLog() di seguito non viene mai chiamato è perché la chiamata dequeueReusableCellWithIdentifier sa creare celle da queste celle prototipo tramite il cellIdentifier definito. Abbastanza utile.

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

si può contare solo sulla UITableView per gestire questa cache di UITableViewCells, e rilasciare in modo appropriato. Quindi è possibile che siano semplicemente in giro perché UITableView non viene rilasciato (anche se penso che tu stia dicendo che lo è).

+0

Mi ha aiutato! @ Chrishr grazie per aver postato questo. In realtà il tuo primo consiglio mi ha aiutato a rintracciare un comportamento inspiegabile. Messaggi come questo rendono lo Stack Overflow ancora migliore! – scrrr

+0

puoi condividere qual è stato il tuo problema? penso che potrei avere qualcosa di simile nella mia app. – Piotr