iphone app con molteplici viste / subviews: la memoria non viene deallocato
-
18-09-2019 - |
Domanda
Ho un'applicazione iPhone che carica viste succesive in un quadro basato su quello spiegato in questo link (fondamentalmente un ViewController
principale che carica / rimuove viste supplementari con un metodo displayView
). Nella mia applicazione Sto usando ONA (il link esempio utilizza viste codificato), anche se in modo ognuno di mia ViewControllers
ha il suo pennino accompagna.
Debug in strumenti spettacoli perdite , ma se entro / lasciare una sezione (ViewController con la sua View.xib), il pennino rimane in memoria in modo che dopo un paio di memoria in / out inizia ad accumularsi.
So che il pennino non viene scaricato perché uno è quasi a livello di codice creato (non roba in IB), mentre un altro ha le immagini e pulsanti creati in IB. Il grande viene caricato per la prima e la piccola uno carichi successivo. Ci si aspetterebbe una riduzione di allocazione in strumenti.
Come si può evitare questo?
La mia struttura è la seguente, con alcuni commenti qui sotto:
`MyAppDelegate.h`
#import <UIKit/UIKit.h>
@class RootViewController;
@interface MyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *viewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet RootViewController *viewController;
-(void) displayView:(int)intNewView;
@end
`MyAppDelegate.m`
#import "MyAppDelegate.h"
#import "RootViewController.h"
@implementation MyAppDelegate
@synthesize window;
@synthesize viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}
-(void) displayView:(int)intNewView {
[viewController displayView:intNewView];
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
@end
Questo controller gestisce carico visualizzazione secondaria / rimuove:
`RootViewController.h`
#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController {
}
- (void) displayView:(int)intNewView;
@end
`RootViewController.m`
#import "RootViewController.h"
#import "ViewController.h"
@implementation RootViewController
UIViewController *currentView;
- (void) displayView:(int)intNewView {
NSLog(@"%i", intNewView);
[currentView.view removeFromSuperview];
[currentView release];
switch (intNewView) {
case 1:
currentView = [[ViewController alloc] initWithNibName:@"View" bundle:nil];
break;
}
[self.view addSubview:currentView.view];
}
- (void)viewDidLoad {
currentView = [[ViewController alloc]
initWithNibName:@"View" bundle:nil];
[self.view addSubview:currentView.view];
[super viewDidLoad];
}
- (void)dealloc {
[currentView release];
[super dealloc];
}
@end
Ci sarebbero tanti case
come ViewControllers
"dettaglio" Ho (in questo momento ho 3 case
ma questo crescerà a 10 o più). Lo scopo di questa struttura è quello di spostare facilmente da una "sezione" della domanda ad un altro (controllore NavBar o il controller TabBar non soddisfare le mie esigenze specifiche).
`ViewController.h`
// Generic View Controller Example
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
UIImageView *_image1;
UIImageView *_image2;
NSTimer *_theTimer;
}
@property (nonatomic, retain) IBOutlet UIImageView *image1;
@property (nonatomic, retain) IBOutlet UIImageView *image2;
@property (nonatomic, retain) NSTimer *theTimer;
@end
`ViewController.m`
#import "ViewController.h"
#import "MyAppDelegate.h"
@synthesize image1 = _image1, image2 = _image2, theTimer = _theTimer;
- (void)loadMenu {
[self.theTimer invalidate];
self.theTimer = nil;
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate displayView:2];
}
-(void)setView:(UIView*)aView {
if (!aView){
self.image1 = nil;
self.image2 = nil;
}
[super setView:aView];
}
- (void)viewDidLoad {
//some code
[super viewDidLoad];
}
- (void)viewDidUnload {
self.image1 = nil;
self.image2 = nil;
}
- (void)dealloc {
NSLog(@"dealloc called");
[self.theTimer invalidate];
[self.theTimer release];
[self.image1 release];
[self.image2 release];
[super dealloc];
}
Si noti la NSLog
in dealloc
. Questo viene chiamato (posso vederlo nella console), ma la memoria necessaria per il pennino non viene liberato (Instruments mostra un aumento di allocazione della memoria al momento di lasciare una sezione, perché un nuovo pennino è caricato).
Ogni aiuto sarà molto apprezzato. Ho provato un milione di cose diverse e non posso ottenere i pennini per scaricare.
Soluzione
Dopo un milione di diversi tentativi finalmente ho corse in questo forum .
Esso afferma:
A quanto pare immagini assegnate in IB vengono caricati in vista di immagine utilizzando
imageNamed
.imageNamed
memorizza le immagini in un modo che li rende scarrabile. Si potrebbe caricare le immagini inviewDidLoad
coninitWithContentsOfFile
e poi assegnarli ai punti di vista.
Da qualche altra parte avevo letto che imageNamed
è il diavolo quindi preferirei non avere le mie immagini vengono caricate in questo modo.
(BTW questo è iPhone OS 3.1 che sto usando)
Quello che ho finito lascia il UIImageView
intatta in IB, ma con un valore .image
vuoto. Il codice modificato è qualcosa di simile:
- (void)viewDidLoad {
NSString *path = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], @"myImageThatBeforeWasAValueinIB.jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
outlet.image = image;
// do the rest of my stuff as it was
[super viewDidLoad];
}
- (void)dealloc {
outlet.image = nil;
[outlet release], outlet = nil;
[super dealloc];
}
E ora tutto funziona come un fascino! La memoria è recuperato quando scaricare un pennino e quando ricevo avvisi di memoria.
Quindi, più o meno, se si dispone di IBOutlets
per UIImageView
s e la memoria è una preoccupazione (è sempre credo), è possibile progettare tutto quello che volete in IB, e quando arriva il momento di collegarli alle prese, rimuovere il riferimento immagine in IB e crearlo dal codice. IB è veramente buono per la posa fuori la vostra applicazione. E avrebbe fatto schifo di avere a che fare tutto ciò che cosa dal codice, ma ho anche trovato questa simpatica utility che converte pennini all'obiettivo codice C anche se non ho ancora testato.
Altri suggerimenti
Hai provato impostare le variabili di uscita a zero in dealloc? Si sono correttamente attuazione del metodo setview, ma si sta impostando le variabili di uscita a zero nel metodo viewDidUnload invece di dealloc. Come discusso qui , è necessario implementare dealloc come segue:
- (void)setView:(UIView *)aView {
if (!aView) { // view is being set to nil
// set outlets to nil, e.g.
self.anOutlet = nil;
}
// Invoke super's implementation last
[super setView:aView];
}
- (void)dealloc {
// release outlets and set outlet variables to nil
[anOutlet release], anOutlet = nil;
[super dealloc];
}
Modifica : se le prese sono UIImageViews, allora potrebbe essere il caso che è necessario fare
anOutlet.image = nil;
perché l'impostazione proprietà immagine esempio del UIImage dovrebbe aumentare il conteggio di conservare esempio del UIImage di 1.