2013-05-07 13 views
9

Mam niestandardowe UIView o nazwie ActivityDetailView, które można utworzyć, a następnie dodać do przewijania w nadrzędnym kontrolerem widoku. Po przydzieleniu tego widoku niestandardowego zajmuje on około 1 MB za każdym razem dodatkowej pamięci, a Instruments pokazuje, że pamięć nigdy nie jest uwalniana, mimo że widok i kontroler widoku rodzica są wywoływane. Dostaję ostrzeżenia o pamięci i aplikacja zostaje ostatecznie zabita, więc oczywiście robię coś złego.Brak pamięci dla MKMapView w/ARC

Aktualizacja informacji na temat w/mapa jest przyczyną, ale muszę poprawkę

w pliku stalówki niestandardowe ActivityDetailView znajduje się mapa, która jest powiększony i wokół miejsca pobytu użytkowników męska. Po usunięciu tego widoku mapy ze stalówki, aby nie rysowała się na ekranie, problemy z alokacją pamięci zniknęły. Jednak oczywiście potrzebuję widoku mapy. Dlaczego dane mapy nie zostałyby zwolnione, gdy widok mapy wykracza poza zakres?

Podczas wyświetlania jest tylko 1 i 1 ActivityDetailViewController przy życiu. Jak tylko wyskoczę z widoku, już nie żyją. Nie ma sensu, jak pamięć rośnie, mimo że obiekty są zabijane, jak pokazano za pomocą instrumentów. Jeśli widoki nadrzędne są deallocowane, dlaczego dane widoku mapy nie są przydzielane?

Co robię źle lub co powinienem sprawdzić?

Oto widok niestandardowy:

@interface ActivityDetailView() 
{ 
    CLLocation *location; 
    __weak id parentViewController; 
    int scrollViewX; 

    ImageUtility *imageUtility; 
} 
@end 

@implementation ActivityDetailView 

-(id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) 
    { 
     NSArray *xibViews = [[NSBundle mainBundle] loadNibNamed:@"ActivityDetailView" owner:nil options:nil]; 
     if ([xibViews count] < 1) return nil; 
     ActivityDetailView * xibView = [xibViews objectAtIndex:0]; 
     [xibView setFrame:frame]; 
     self = xibView; 
    } 
    return self; 
} 

- (id)initWithLocation:(CLLocation *)loc parentController:(id)parent 
{ 
    self = [self initWithFrame:CGRectMake(0, 0, 320, 1000)]; 
    if (self) 
    { 
     imageUtility = [ImageUtility sharedManager]; 

     location = loc; 
     parentViewController = parent; 
     scrollViewX = 0; 

     [self centerMapForActivityLocation]; 
     [self addPhotoButtonWithImageNamed:@"addActivityPhoto.png" target:parentViewController selector:@selector(addPhotoToActivity:)]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    NSLog(@"ActivityDetailView was dealloced"); 
} 

W regulatorze Zobacz główne:

@interface ActivityDetailViewController() 
{ 
    ActivityDetailView *detailView; 
} 
@end 

@implementation ActivityDetailViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // Some code left out for clarity 

    [self setupView]; 
} 

- (void)didReceiveMemoryWarning 
{ 
    NSLog(@"Purging image cache"); 
    [[ImageUtility sharedManager] purgeCache]; 
} 

- (void)dealloc 
{ 
    // These essentially do nothing to help the problem 
    detailView.mapView = nil; 
    [detailView removeFromSuperview]; 
    detailView = nil; 
    self.scrollView = nil; 

    NSLog(@"ActivityDetailViewController was dealloced"); 
} 

- (void)setupView 
{  
    // Add the activity detail view to the scroll view 
    detailView = [[ActivityDetailView alloc] initWithLocation:self.activityLocation parentController:self]; 
    [self.scrollView addSubview:detailView]; 
    self.scrollView.contentSize = detailView.frame.size; 

    // Setup the map view 
    detailView.mapView.delegate = self; 
    MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init]; 
    annotation.coordinate = self.activityLocation.coordinate; 
    [detailView.mapView addAnnotation:annotation]; 
    if (self.activity.mapImageName) { 
     detailView.mapView.scrollEnabled = YES; 
     detailView.mapView.zoomEnabled = YES; 
    } else { 
     detailView.mapView.scrollEnabled = NO; 
     detailView.mapView.zoomEnabled = NO; 
    } 

    // Add the weather area to the view 
    dayView = [[DailyButtonView alloc] initWithFrame:CGRectMake(0, -17, 60, 70)]; 
    dayView.hidden = YES; 
    [detailView.weatherView addSubview:dayView]; 
} 

Oto obraz z instrumentami pokazując, że większość pamięci jest z załadunkiem stalówki

Instruments screenshot

Nie mam niczego g do buforowania w tej chwili. Widok jest w zasadzie widokiem przewijania z widokiem mapy wewnątrz i niektórymi etykietami oraz kilkoma widokami tabel. Widoki tabel nie są wypełnione czymkolwiek i nie ładuję żadnych obrazów w widoku innym niż obraz tła dla przewijania, ale to powinno zostać zwolnione, gdy widok robi.

+1

Nie jestem pewien, czy to jest problem (i nie powinien być). Ale te dwie linie kodu są nieprawidłowe detailView = nil; [detailView removeFromSuperview] ;. Powinieneś wywołać removeFromSuperView, a następnie ustawić go na zero. – Cosmin

+0

@ Cosmin cóż, myślę, że to nie ma znaczenia, nie dostałby dealloc, gdyby był jeszcze dodany do innego widoku ... Domyślam się, że nasz OP właśnie próbował czegoś i zostawił go. –

+0

@Cosmin As Grady Powiedziałem, że próbowałem czegoś w tym momencie, aby zobaczyć, czy to coś zmieniło, ale tak się nie stało. – ZeNewb

Odpowiedz

22

Jak się okazało, jest znany błąd z iOS 6 i MKMapView nie zwalniając poprawnie jego pamięci.Niestety nie ma prawdziwej poprawki poza próbą wymuszenia widoku mapy, aby usunąć pamięć podręczną, która nie ma tak wielkiego wpływu na zwolnienie pamięci.

Dziwne jest to, że nawet gdy aplikacja otrzymuje ostrzeżenia z pamięci, pamięć podręczna widoku mapy nadal nie jest prawidłowo czyszczona. Ostatecznie aplikacja staje się niestabilna i jest zabijana przez system operacyjny.

Apple ostatnio robi się bardzo zaniedbany. To jest drugi błąd z MKMapView, z którym się zetknąłem (drugi jest mapViewDidFinishLoadingMap: nazywany wcześniej) i oba błędy były naprawdę oczywiste, aby złapać, jeśli właśnie wykonały testy wydajności i poprawności.

Oto kod, który może pomóc:

- (void)viewDidDisappear:(BOOL)animated 
{ 
    [super viewDidDisappear:animated]; 

    // This is for a bug in MKMapView for iOS6 
    [self purgeMapMemory]; 
} 

// This is for a bug in MKMapView for iOS6 
// Try to purge some of the memory being allocated by the map 
- (void)purgeMapMemory 
{ 
    // Switching map types causes cache purging, so switch to a different map type 
    detailView.mapView.mapType = MKMapTypeStandard; 
    [detailView.mapView removeFromSuperview]; 
    detailView.mapView = nil; 
} 

Drugą rzeczą, jaką można zrobić, to użyć jednej instancji MKMapView całej całej aplikacji, a które powinny pomóc zminimalizować, ile pamięci jest przydzielana Każdorazowe widok jest wyświetlany, ponieważ widok mapy zostanie już przydzielony.

+0

Świetna sugestia, ponowne użycie jednej instancji MKMapView zamiast każdorazowego przydzielania/dealloc'owania nowych. Mam podobne problemy ... Czy wiemy, czy ten błąd został naprawiony od iOS 9+? –

+1

@ AdamW.Dennis Nie pracowałem z mapami Apple w ciągu kilku lat, więc możesz sprawdzić na forum OpenRadar lub Apple dev, czy możesz znaleźć więcej informacji, np. https://openradar.appspot.com/search?query=mkmapview+memory – iwasrobbed

0

To naprawdę nie jest najlepszy sposób tworzenia niestandardowego widoku z pliku nib. Sugeruję, aby dodać widok, który bierzesz z pliku .nib jako widok własny. Kod powinien wyglądać mniej więcej tak.

+0

Niestety nie ma to znaczenia dla alokacji pamięci. – ZeNewb

+0

ale powinieneś rozważyć, co on mówi, pomoc zawsze jest doceniana –

0

Prawdopodobnie napotykasz problem z pamięcią podręczną.

Jeśli używasz wielu zdjęć, dobrym rozwiązaniem może być usunięcie tych zdjęć z pliku XIB i dodanie go za pomocą kodu. Lub jeśli załadować obrazy za pomocą [UIImage imageNamed:@""]

Ale używać coś takiego:

NSData* dataImg = [NSData dataWithContentsOfFile:@"yourfile.png"]; 
UIImage* img = [UIImage imageWithData:dataImg]; 

Aby lepiej pomóc muszę wiedzieć, jak zacząć tabele, a także innych cech.

+0

Nie mam nic do buforowania w tej chwili. Widok jest w zasadzie widokiem przewijania z widokiem mapy wewnątrz i niektórymi etykietami. Widoki tabel nie są wypełnione czymkolwiek i nie ładuję żadnych obrazów w widoku innym niż obraz tła dla przewijania, ale to powinno zostać zwolnione, gdy widok robi. – ZeNewb

+0

Nop, obraz, który dodasz do widoku, używając imagenamed lub z xib, jest zapisywany w pamięci podręcznej, a czasami może spowodować awarię aplikacji. – ggrana

+0

Obraz tła jest dodawany do stalówki i jest to ten sam obraz tła, który jest używany w całej aplikacji, więc nie sądzę, że to jest problem. Żaden z pozostałych widoków nie ma problemów z przydziałem, tylko ten robi. – ZeNewb