2009-09-27 21 views
5

Mam aplikację na iPhone'a, która ładuje kolejne widoki w strukturze opartej na tym explained in this link (w zasadzie główny ViewController, który ładuje/usuwa dodatkowe widoki za pomocą metody displayView). W mojej aplikacji używam NIBs (przykładowy link używa zakodowanych widoków), więc każdy z moich ViewControllers ma dołączoną końcówkę.Aplikacja dla iPhone'a z wieloma widokami/podskokami: pamięć nie jest wycofywana.

debugowanie instrumentami pokazuje ma wycieków ale gdybym wejść/opuścić sekcję (ViewController z View.xib), Stalówka pozostaje w pamięci tak po kilka w/out pamięć zaczyna gromadzić.

Wiem, że stalówka nie jest rozładowywana, ponieważ jedna jest prawie programowana (brak elementów w IB), a druga ma obrazy i przyciski utworzone w IB. Duży ładowany jest pierwszy, a mały ładowany dalej. Spodziewałbyś się zmniejszenia alokacji w Instrumentach.

Jak mogę temu zapobiec?

Moja struktura wygląda następująco, z kilkoma uwagami poniżej:

`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 

Sterownik ten obsługuje obciążenie subview/Usuwa:

`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 

Nie byłoby tyle case jako "Detail" ViewControllers I mieć (teraz mam 3 case, ale ten wzrośnie do 10 lub więcej). Celem tej struktury jest łatwe przejście z jednej "sekcji" aplikacji do drugiej (kontroler NavBar lub kontroler TabBar nie odpowiada moim potrzebom).

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

Zawiadomienie NSLog w dealloc. To jest wywoływane (widzę to w konsoli), ale pamięć potrzebna na stalówkę nie jest zwalniana (Instrumenty pokazują zwiększenie alokacji pamięci podczas opuszczania sekcji, ponieważ ładowany jest nowy stalówka).

Każda pomoc zostanie bardzo doceniona. Wypróbowałem milion różnych rzeczy i nie mogę zmusić stalówki do rozładowania.

Odpowiedz

6

Po milionie różnych prób w końcu natknąłem się na this forum.

Stwierdza:

Najwyraźniej obrazy przypisane w IB są ładowane do widoków graficznych wykorzystujących imageNamed.imageNamed buforuje obrazy w sposób, który powoduje, że są nie do załadowania. Można załadować obrazy w formacie viewDidLoad za pomocą initWithContentsOfFile, a następnie przypisać je do widoków.

Gdzieś indziej przeczytałem, że imageNamed is the devil, więc wolałbym, aby moje obrazy nie ładowały się w ten sposób.

(BTW to jest iPhone OS 3.1 używam)

Co skończyło się opuszcza UIImageView nienaruszone w IB ale z pustym .image wartości. Zmodyfikowany kod wygląda tak:

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

A teraz wszystko działa jak czar! Pamięć jest odzyskiwana po zwolnieniu stalówki i otrzymaniu ostrzeżeń dotyczących pamięci.

Tak więc, jeśli masz IBOutlets dla UIImageView s i pamięci jest problemem (to zawsze jest chyba), możesz zaprojektować wszystko, co chcesz w IB i gdy nadejdzie czas, aby podłączyć je do gniazd, usunąć odniesienie obrazu w IB i utwórz go z kodu. IB jest naprawdę dobry do układania aplikacji. Byłoby to wyssane, żeby zrobić to wszystko za pomocą kodu, ale znalazłem kod this nice utility that converts nibs to objective c, chociaż jeszcze go nie testowałem.

+0

Możesz pobrać moją wersję frameworka z poprawką tutaj: http://www.mauriciogiraldo.com/blog/2009/10/09/multiples-views-no-jerarquicas-en-iphone/ Przewiń w dół, aby zobaczyć angielski tłumaczenie. – mga

0

Czy próbowałeś ustawić zmienne wyjściowe na zero w dealloc? Poprawnie implementujesz metodę setView, ale ustawiasz zmienne wyjściowe na zero w metodzie viewDidUnload zamiast dealloc. Jak omówiono here, należy wdrożyć dealloc następująco:

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

EDIT: jeżeli wyloty są UIImageViews, to może się zdarzyć, że trzeba zrobić

anOutlet.image = nil; 

ponieważ ustawienie UIImage na właściwość obrazu instancji powinna zwiększyć liczbę zatrzymań instancji UIImage o 1.

+0

Dziękuję za odpowiedź. Jak by to działało z "_name" ivars i "name" właściwości? [self.anOutlet release], self.anOutlet = nil zatrzymuje się z komunikatem "message wysłany do deallocated instance" i [self.anOutlet release], _anOutlet = zero nie ma żadnego efektu (alokacja-mądrość) – mga

+0

... czy powinienem po prostu nie używać _name ivars i właściwości nazwy? – mga

+0

[_name release], _name = nil; nie łamie się, ale nie zmienia się żadna zmiana – mga