2009-09-27 11 views
5

tengo una aplicación de iPhone que carga vistas sucesivas en un marco basado en el explained in this link (básicamente un principal ViewController que las cargas/elimina vistas adicionales con un método displayView). En mi aplicación estoy usando NIBs (el enlace de ejemplo usa vistas codificadas), así que cada uno de mis ViewControllers tiene su plumín de acompañamiento.aplicación para el iPhone con múltiples puntos de vista/subvistas: La memoria no se está desasignado

Depuración de instrumentos muestra no hay fugas pero si entro/dejar una sección (ViewController con su View.xib), la punta se mantiene en la memoria para después de unos pocos memoria entradas/salidas comienza a acumularse.

Sé que no se está descargando el plumín porque uno está casi programáticamente creado (nada en IB) mientras que otro tiene imágenes y botones creados en IB. El grande se carga primero y el pequeño se carga a continuación. Esperaría una reducción en la asignación en Instrumentos.

¿Cómo puedo evitar esto?

Mi estructura es la siguiente, con unos pocos comentarios a continuación:

`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 

Este controlador maneja la carga subvista/quita:

`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 

No habría tantos case como "detalle" ViewControllers I tener (ahora tengo 3 case pero esto crecerá a 10 o más). El propósito de esta estructura es pasar fácilmente de una "sección" de la aplicación a otra (el controlador NavBar o el controlador TabBar no se ajustan a mis necesidades específicas).

`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]; 
} 

Aviso del NSLog en dealloc. Se está llamando (puedo verlo en la consola) pero la memoria necesaria para el plumín no se libera (Instruments muestra un aumento en la asignación de memoria al salir de una sección, porque se carga un plumín nuevo).

Cualquier ayuda será muy apreciada. He intentado un millón de cosas diferentes y no puedo descargar las puntas.

Respuesta

6

Después de un millón de intentos diferentes, finalmente me encontré con this forum.

Afirma:

Al parecer, las imágenes asignadas en IB se cargan en vistas de imágenes utilizando imageNamed.imageNamed almacena en caché las imágenes de forma que sean descargables. Puede cargar las imágenes en viewDidLoad con initWithContentsOfFile y luego asignarlas a las vistas.

En otro lugar he leído que imageNamed is the devil así que prefiero que mis imágenes no se carguen de esa manera.

(Por cierto este es el iPhone OS 3.1 que estoy usando)

Lo que terminé abandona el UIImageView intacto en IB, pero con un valor vacío .image. El código modificado es algo así como:

- (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]; 
} 

Y ahora todo funciona como un encanto! La memoria se recupera cuando descargo una punta y cuando recibo advertencias de memoria.

Tanto si tienes IBOutlets para UIImageView s y la memoria es una preocupación (siempre es así, supongo), puedes diseñar todo lo que quieras en IB y cuando llegue el momento de conectarlos a las tomas, elimina la referencia de la imagen en IB y crearlo desde el código. IB es realmente bueno para diseñar tu aplicación. Sería una mierda tener que hacer todo eso por código, pero también encontré el código this nice utility that converts nibs to objective c aunque aún no lo he probado.

+0

Puede descargar mi versión del marco con la corrección aquí: http://www.mauriciogiraldo.com/blog/2009/10/09/multiples-views-no-jerarquicas-en-iphone/ Desplácese hacia abajo para el inglés traducción. – mga

0

¿Intentó establecer sus variables de salida en nulo en dealloc? Está implementando correctamente el método setView, pero está estableciendo las variables de salida en nil en el método viewDidUnload en lugar de dealloc. Como se discutió here, debe implementar dealloc de la siguiente manera:

- (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]; 
} 

EDITAR: si las salidas son UIImageViews, entonces puede ser el caso de que lo que necesita hacer

anOutlet.image = nil; 

porque la configuración UIImage de la propiedad de imagen de instancia debe aumentar el conteo retenido de la instancia de UIImage por 1.

+0

Gracias por su respuesta. ¿Cómo funcionaría esto con las propiedades "name" y "name"? [self.anOutlet release], self.anOutlet = nil se detiene con un mensaje de "mensaje enviado a instancia desasignada" y [self.anOutlet release], _anOutlet = nil no tiene ningún efecto (asignación) – mga

+0

... o debería simplemente no usar _name ivars y propiedades de nombre? – mga

+0

[_name release], _name = nil; no se rompe pero no cambia la asignación, ya sea – mga

Cuestiones relacionadas