2009-02-26 12 views
5

Już prawie rozumiem proste liczenie odwołań/zarządzanie pamięcią w Objective-C, jednak mam trudny czas z następującym kodem. Zwolniłem mutableDict (skomentowałem w poniższym kodzie) i powoduje to szkodliwe zachowanie w moim kodzie. Jeśli pozwolę, aby pamięć przeciekała, działa zgodnie z oczekiwaniami, ale to oczywiście nie jest odpowiedź tutaj. ;-) Czy ktokolwiek z was bardziej doświadczonych ludzi byłby na tyle uprzejmy, by wskazać mi właściwy kierunek, w jaki sposób mogę ponownie napisać którąś z tych metod, aby lepiej radzić sobie z moim śladem pamięci? Głównie z tym, jak zarządzam NSMutableDictionary * mutableDict, ponieważ jest to wielki winowajca. Chciałbym zrozumieć problem, a nie tylko skopiować/wkleić kod - więc niektóre komentarze/opinie są idealne. Dziękuje wszystkim.Objective-C: Naprawianie zarządzania pamięcią w metodzie

- (NSArray *)createArrayWithDictionaries:(NSString *)xmlDocument 
           withXPath:(NSString *)XPathStr { 

    NSError *theError = nil; 
    NSMutableArray *mutableArray = [[[NSMutableArray alloc] init] autorelease]; 
    //NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init]; 
    CXMLDocument *theXMLDocument = [[[CXMLDocument alloc] initWithXMLString:xmlDocument options:0 error:&theError] retain]; 
    NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError]; 
    int i, j, cnt = [nodes count]; 
    for(i=0; i < cnt; i++) { 
     CXMLElement *xmlElement = [nodes objectAtIndex:i]; 
     if(nil != xmlElement) { 
      NSArray *attributes = [NSArray array]; 
      attributes = [xmlElement attributes]; 
      int attrCnt = [attributes count]; 
      NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init]; 
      for(j = 0; j < attrCnt; j++) { 
       if([[[attributes objectAtIndex:j] name] isKindOfClass:[NSString class]]) 
        [mutableDict setValue:[[attributes objectAtIndex:j] stringValue] forKey:[[attributes objectAtIndex:j] name]]; 
       else 
        continue; 
      } 
      if(nil != mutableDict) { 
       [mutableArray addObject:mutableDict]; 
      } 
      [mutableDict release]; // This is causing bad things to happen. 
     } 
    } 

    return (NSArray *)mutableArray; 
} 

Odpowiedz

5

Oto odpowiednik przepisanie kodu:

- (NSArray *)attributeDictionaries:(NSString *)xmlDocument withXPath:(NSString *)XPathStr { 
    NSError *theError = nil; 
    NSMutableArray *dictionaries = [NSMutableArray array]; 
    CXMLDocument *theXMLDocument = [[CXMLDocument alloc] initWithXMLString:xmlDocument options:0 error:&theError]; 
    NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError]; 

    for (CXMLElement *xmlElement in nodes) { 
     NSArray *attributes = [xmlElement attributes]; 
     NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary]; 
     for (CXMLNode *attribute in attributes) { 
      [attributeDictionary setObject:[attribute stringValue] forKey:[attribute name]]; 
     } 

     [dictionaries addObject:attributeDictionary]; 
    } 

    [theXMLDocument release]; 
    return attributeDictionaries; 
} 

Wskazówka ja tylko zrobiłem liczenia odniesienia na theXMLDocument. A to dlatego, że tablice i słowniki żyją poza zasięgiem tej metody. Metody klasy array i dictionary tworzą autorejestrowane instancje obiektów NSArray i NSMutableDictionary. Jeśli osoba dzwoniąca nie zachowuje ich w sposób jawny, zostaną one automatycznie zwolnione w następnej rundzie pętli zdarzeń aplikacji.

  • Usunąłem też kod, który nigdy nie został wykonany. Metoda CXMLNodename mówi, że zwraca ciąg, więc test zawsze będzie prawdziwy.
  • Jeśli mutableDict jest nil, masz większe problemy. Lepiej, żeby wyrzucił wyjątek niż cicho zawieść, więc też usunąłem ten test.
  • Użyłem również względnie nowej składni wyliczeniowej for, która eliminuje zmienne licznika.
  • Zmieniłem nazwę niektórych zmiennych, a metoda jest nieco bardziej kakao.Kakao różni się od większości języków tym, że ogólnie uważane jest za niepoprawne używanie czasownika takiego jak "twórz", chyba że chcesz, aby osoba dzwoniąca była odpowiedzialna za zwolnienie zwracanego obiektu.
  • Nic nie zrobiłeś z theError. Powinieneś to sprawdzić i zgłosić błąd, lub przepuścić nil, jeśli nie zamierzasz tego sprawdzić. Nie ma sensu, aby aplikacja budowała obiekt błędu, którego nie będziesz używał.

Mam nadzieję, że pomoże ci to wskazać właściwy kierunek.

+0

Alex, teraz sprawdzam kod, ale od razu widzę problem polegający na tym, że funkcja attributeDictionaries wyjdzie poza zakres, zanim będzie można go zwrócić. –

+0

Myślę, że właśnie zrobił literówkę (i nie mam wystarczającej liczby przedstawicieli, aby ją edytować) - myślę, że ostatnia linia powinna być "słowniki zwrotne". Instancja attributeDictionary jest dodawana do tej tablicy, więc nie wykracza poza zakres. – erikprice

+0

Yup. Erik ma rację. Nie zauważyłem tego wystarczająco szybko. – Alex

1

Dobrze, uwalniając mutableDict naprawdę nie powinno być przyczyną problemów, ponieważ linia nad nim (dodawanie mutableDict do mutableArray) będzie go zatrzymać automatycznie. Chociaż nie jestem pewien, co dokładnie jest nie tak z kodem (nie określono, co „złe rzeczy” znaczy), istnieje kilka ogólnych rzeczy proponuję:

  1. Nie autorelease mutableArray prawo z dala. Niech to będzie zwykła instrukcja alokacji/init i autorelease ją, gdy ją zwrócisz ("return [autoodlease mutableArray];").

  2. theXMLDocument jest nieszczelny, należy go zwolnić przed powrotem. Ponadto nie musisz go zachować tak jak Ty. alloc/init wykonuje zadanie, rozpoczynając odliczanie zatrzymania obiektu na 1, zatrzymując je ponownie, po prostu zapewnia, że ​​wycieknie na zawsze. Pozbądź się retain i zwolnij go przed powrotem i nie wycieknie.

  3. Just a wskazówka: upewnij się, że zachowują wartości zwracanej tej metody podczas używania go gdzie indziej - wynik został autoreleased jak nie gwarantuje się dookoła, kiedy trzeba, chyba że wyraźnie zachowują/uwolnienia go gdzieś .

przeciwnym razie ten kod powinien praca. Jeśli nadal nie działa, to jeszcze jedną rzeczą, którą wypróbowałbym, może być wykonanie [mutableArray addObject: [mutableDict copy]], aby upewnić się, że mutableDict nie spowoduje żadnych problemów, gdy zostanie zwolniony.

+0

"Nie autorelease mutableArray od razu ... autorelease go, gdy go zwrócisz ..." Nie zgadzam się. Jeśli nie widzę autorelease w tej samej linii, muszę poszukać, aby upewnić się, że nie wyciekła. –

0

W Memory Management Programming Guide pod temat Powracające obiektów z Metod (przewinąć kawałek), istnieje kilka prostych przykładów, w jaki sposób powrócić obiekty z metody z prawidłowym managment pamięci.

Powiązane problemy