2012-06-26 12 views
5

Ich habe einen UIViewController, der modal präsentiert wird. Wenn ich das Speicherzuweisungen-Instrument beobachte, erhöht sich die Speichernutzung, wenn die Ansicht dargestellt wird, aber wenn sie beendet wird, wird der Speicher nicht freigegeben. Wenn ich die Ansicht öffne und schließe, wird der Speicher immer höher. Instrumente melden kein Speicherleck! Was könnte das verursachen? Der View-Controller-Code ist unten (ich habe den didSelectRow-Code übersprungen). Dealloc wird immer aufgerufen.Möglicher Speicherverlust in UIViewController mit UITableView

EDIT - Ich bin mit 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 ------------- Etwas nicht scheint Recht haben. Ich habe einen NSLog zur Zellenallokierung hinzugefügt und er wird nie aufgerufen, obwohl die Zellen erstellt wurden!

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

EDIT 1. Juli ------ Ich habe eine Navigation Controller hinzugefügt und jetzt Push anstelle von modalen und dieses Problem ist noch da. Ich habe mit Aufnahmen Shapes gemacht, indem ich ein paar Mal zwischen den Ansichten hin- und herwechsle und es scheint, als ob die Zellen immer noch herumhängen, da dieser Screenshot zeigt, dass Gestenerkennung noch von einer vorherigen Ladung der Ansicht besteht. screen shot

+0

Innen viewWillAppear verwenden Sie performOnMainThread. Dies wird nicht benötigt, da viewWillAppear im Hauptthread passiert. –

+0

Ich habe nur diese Methode verwendet, also kann ich waitUntilDone: YES einstellen, damit ich jetzt die Arrays gefüllt habe, bevor ich die Tabelle gezeichnet habe. – Darren

+0

Versuchen Sie Folgendes: [self workOutFullUseToDisplay]. Sie erkennen, Objective-C ist sequenziell richtig? –

Antwort

3

Es ist, weil Sie Ihre IBOutlets als weak verwendet, statt strong zu verwenden.

Ich glaube tatsächlich, dass dies ein Fehler in der XCode-Umgebung ist, da es Sie vor dieser Art von Verhalten warnen sollte.

Als eine empfehlenswerte Vorgehensweise würde ich vorschlagen, XCode die IBOutlets generieren zu lassen, indem Sie die Ansichten in den Code im Interface Builder ziehen, um solche ärgerlichen Fallstricke zu vermeiden.

+0

Ja, ich sagte das oben selbst. Eine Erklärung, warum sollte gut sein.Ich habe vorhin gelesen, um schwache Links für IBOutlets zu verwenden.Ich lasse XCode die IBOutlets generieren, aber in dem Popup haben Sie die Wahl von stark oder schwach. Außerdem dachte ich stark wäre mehr Chance hat, etwas hinter als schwach zu verlassen, dachte ich ein schwaches referenzierte Objekt würde entfernt werden, wenn die Ansicht entfernt wird. – Darren

+1

Es hängt tatsächlich davon ab. Sie sollten starke Links für IBOutlets, die dem FileOwner gehören, und schwache Links für IBOutlets, die von Subviews verwendet werden, verwenden. https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/LoadingResources/CcoaNibs/CocoaNibs.html # // apple_ref/doc/uid/10000051i-CH4-SW6 Bitte beachten Sie die Teile, die sich auf iOS beziehen (und nicht OS X) – Gilbert

+2

Ich glaube, dass in Ihrem speziellen Fall die UITableView Zellen mit einer starken zugeordnet wurden Link und referenzierte die Tabellenansicht (Superview). Während auf der anderen Seite, die UITableview (die schwach ist), referenzierte auch die Zellen - die keiner von ihnen zu dealloc – Gilbert

0

[EDIT]

In Ihrer viewWillAppear Methode haben Sie ausgedruckt, um zu sehen, wie oft Sie durch Ihre else-Klausel zu bewegen. Für mich scheint es, dass Sie Ihre Methoden workOutSingleUseToDisplay und workOutFullUseToDisplay aufrufen. Jedes Mal, wenn Sie diese aufrufen, weisen Sie _singleUseArray und _fullUseArray zu. Nur weil Sie in eine Ansicht hinein- und wieder herausgehen, heißt das nicht, dass sie dealloc aufruft oder dass sie Ihre aktuellen Arrays automatisch freigibt. Was ich denke, dass Sie sehen, ist, dass wenn Sie aus Ihrer Sicht bewegen, es diese zwei Arrays nicht freigibt, aber versucht, sie neu zuzuweisen.

[ORIGINAL] Nun, in Ihrer ViewDidLoad, führen Sie eine Alloc. In Ihrem Dealloc sehe ich keine [Fußzeile]. Das könnte dein Leck sein !!! Ich sehe auch nicht die Freigabe Ihrer _singleUseArray oder _fullUseArray Arrays

+0

Ich verwende ARC, kann also nicht manuell freigeben. Ich habe versucht, _singleUseArray = nil hinzuzufügen; _fullUseArray = Null; Fußzeile = Null; zur Dealloc aber keine Änderung. – Darren

+1

Sie sollten über ARC in Ihrem ursprünglichen Beitrag erzählen, weil ich denke, dass es für die Antwort bestimmt. – Martin

+0

Entschuldigung. Ich werde es der Frage hinzufügen. Ich habe die 2 Array Alloc auf die ViewDidLoad verschoben, so dass sie definitiv nur einmal aufgerufen werden, aber die Speichermuster sind immer noch die gleichen. Die View-Controller Dealloc wird bei jedem Schließen aufgerufen. Irgendwelche anderen Ideen? – Darren

0

Ich bin nicht sicher, ob ich den anwer bekam, aber es ist etwas seltsam in Ihrem Code:

Ihre verwenden schwache Eigenschaften:

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

Aber nach the doc (Suche "schwach"), weak property ist sehr ähnlich zu assign.

In Sie dealloc, Sie haben

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

Ich bin ziemlich sicher, dass die erzeugte Setter dieser Eigenschaften sie gar nicht entbindet!

Aber wenn Sie Ihre Eigenschaften wie erklären:

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

die erzeugte Setter wie

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

Und Ihre Eigenschaften freigegeben würde wäre.

+0

Ich habe tatsächlich nur die self.tableView = nil und self.starCountLbl = nil hinzugefügt, um dealloc zu löschen, während ich versuche herauszufinden, was nicht freigegeben wird. Es wird nicht wirklich benötigt, da es sich ohnehin um schwache Referenzen handelt und wenn der View-Controller freigegeben wird. – Darren

0

Verwenden Sie das Leaks-Instrument mindestens, um Speicherlecks zu überwachen. Das Allocations-Instrument zeigt die Speicherlecks nicht an. Wenn Sie Analyse ausführen, sehen Sie die Linien, die möglicherweise die Lecks verursachen.

Dies ist Ihr Code:

Rückgabewert

:

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

Sie sehen, cell nicht nil sein würde ... Das in der API-doc für dequeueReusableCellWithIdentifier: angegeben Ein UITableViewCell-Objekt mit dem zugeordneten Bezeichner oder nil, wenn kein solches Objekt in der Warteschlange für wiederverwendbare Zellen vorhanden ist.

Wie auch immer, wenn es Lecks vorhanden sind, vielleicht sind sie sehr viel, verursacht durch:

_singleUseArray = [[NSMutableArray alloc] init]; 

und

_fullUseArray = [[NSMutableArray alloc] init]; 

Wenn Sie

erklärt
NSMutableArray *_singleUseArray; 
NSMutableArray *_fullUseArray; 

Ich denke, durch Standard beide wurden mit einer __strong Qualif zugewiesen ier. Ich bin nicht wirklich sicher, aber das könnte die wahre Ursache des Problems sein. Wie wäre es, dies stattdessen zu erklären?

NSMutableArray * __weak _singleUseArray; 
NSMutableArray * __weak _fullUseArray; 

, auch vor

_singleUseArray = [[NSMutableArray alloc] init]; 

erklärt und

_fullUseArray = [[NSMutableArray alloc] init]; 

, wie man zuerst nil Zuordnung des vorherigen Bezug zu entfernen?

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

und

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

Ich habe das Leak-Tool verwendet und es zeigte hier kein Leck, obwohl der Speicher jedes Mal erhöht wird, wenn die Anzeige geladen wird. – Darren

+0

Nimmt das Array init aus, hat immer noch das Problem :-( – Darren

+0

Ich bekomme 1 Speicherleck zeigen, aber ich glaube nicht, dass es verwandt ist. Es sagt verantwortliches frame = [NSURL (NSURL) Pfad] und wenn ich es anklicke Es sieht so aus, als ob es etwas mit dem Laden des Storyboards zu tun hat. – Darren

2

Sieht aus wie Sie haben schon einige Möglichkeiten, um dieses gefunden, aber nur für den Fall, das hilft:

1) Stellen Sie sicher, dass Zombies nicht bekommen eingeschaltet, während Sie das Debuggen, da diese Objekte verursacht, hänge herum, nachdem du denkst, dass sie rückgängig gemacht werden sollen (Bearbeite Schema -> Ausführen -> Diagnose).

2) Sie verwenden ARC und so nehme ich Storyboards oder zumindest Prototyp UITableView Zellen in Ihrem Storyboard/NIB? Wenn dies der Fall ist, ist der Grund dafür, dass Ihr NSLog() unten nie aufgerufen wird, dass der Aufruf dequeueReusableCellWithIdentifier Zellen aus diesen Prototypzellen über den definierten cellIdentifier erzeugen kann. Ziemlich praktisch.

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

Sie müssen sich auf die UITableView verlassen, um diesen Cache von UITableViewCells zu verwalten, und sie entsprechend freigeben. Es ist also möglich, dass sie nur herumhängen, weil dein UITableView nicht veröffentlicht wird (obwohl ich denke, dass du es sagst).

+0

Es half mir erlaubt! @ChrisH danke für das Posten. Tatsächlich hat mir dein erster Tipp geholfen, ein unerklärliches Verhalten aufzuspüren. Solche Beiträge machen Stack Overflow noch besser! – scrrr

+0

können Sie teilen, was war dein Problem? Ich denke, ich könnte etwas Ähnliches in meiner App haben. – Piotr

Verwandte Themen