2011-12-16 23 views
8

Wciąż próbuję zrozumieć ten fragment kodu, który znalazłem w projekcie, nad którym pracuję, w którym facet, który go stworzył, opuścił firmę, zanim zdążyłem zapytać.Co sądzisz o tym kodzie w Objective-C, który iteruje, zachowuje liczbę i wywołuje wydanie w każdej iteracji?

Jest to kod:

-(void)releaseMySelf{ 
    for (int i=myRetainCount; i>1; i--) { 
     [self release]; 
    } 
    [self autorelease]; 
} 

O ile mi wiadomo, w Objective-C modelu zarządzania pamięcią, pierwsza zasada jest taka, że ​​obiekt, który przydziela inny obiekt, jest również odpowiedzialny, aby zwolnić go w przyszłość. Z tego powodu nie rozumiem znaczenia tego kodu. Czy jest jakieś znaczenie?

+4

Myślę, że "Święta krowa!" – albertamg

+20

Nic dziwnego, że opuścił firmę; Kod taki jak ten jest pewnym znakiem, że był w drodze nad jego głową, nie miał pojęcia, jak napisać aplikację i nie ma możliwości poznania właściwych sposobów. Gdybym odkrył, że w projekcie (i korzystałem z usług firmy konsultingowej, która "posprzątała" w niespokojnych projektach), natychmiast rzuciłbym czerwoną flagę i zażądał, aby każda linia kodu napisana przez tę osobę została sprawdzona . (Wygląda również na to, że utrzymuje liczbę zatrzymań oddzielnie od rzeczywistej liczby zatrzymań obiektu ... 2 czerwone flagi na tej grze.) – bbum

+9

Myślę, że "chcę wiedzieć, kto to napisał, więc mogę być pewny, że nigdy z nim nie pracuję " –

Odpowiedz

17

Autor próbuje obejść, nie rozumiejąc zarządzania pamięcią. Zakłada on, że obiekt ma liczbę zatrzymań zwiększoną o każde zatrzymanie, a więc próbuje ją zmniejszyć, wywołując tę ​​liczbę wydań. Prawdopodobnie nie zaimplementował "jest również odpowiedzialny za wypuszczenie go w przyszłości". część twojego zrozumienia.

Jednak wiele odpowiedzi tutaj, np. here i here i here.

Przeczytaj Apple's memory management concepts.

Pierwszy link zawiera cytat z Apple

Sposób retainCount nie uwzględnia jakichkolwiek nieuregulowanych autorelease wiadomości wysyłane do odbiornika.

Ważne: Metoda ta jest zazwyczaj bez wartości w debugowania pamięci zarządzanie wystawia. Ponieważ dowolna liczba obiektów szkieletowych może mieć obiekt zachowywany obiekt w celu przechowywania odniesień do niego, podczas gdy w pulach autoreleaseów w tym samym czasie mogą znajdować się dowolne liczby odroczonych wydań na obiekcie, jest bardzo mało prawdopodobne, że można uzyskać przydatne informacje z tej metody. Aby zrozumieć podstawowe zasady zarządzania pamięcią, których musisz przestrzegać, przeczytaj "Reguły zarządzania pamięcią ". Aby zdiagnozować problemy z zarządzaniem pamięcią, użyj odpowiedniego narzędzia: Analizator statyczny LLVM/Clang może zwykle znaleźć problemy z zarządzaniem pamięcią nawet przed uruchomieniem programu. Obiekt Object Alloc w aplikacji Instruments (patrz Podręcznik użytkownika Instruments) może śledzić przydział i zniszczenie obiektu . Shark (patrz Shark User Guide) również przypisuje pamięci do profili (wśród wielu innych aspektów twojego programu ).

+3

Na marginesie zalecam usunięcie tego fragmentu kodu i uruchomienie kodu za pomocą analizatora statycznego. Powinien pokazać, gdzie powinny zostać wydane obiekty. – Eimantas

3

To jest brudny hack wymuszający zwolnienie pamięci: jeśli reszta twojego programu jest napisana poprawnie, nigdy nie musisz robić czegoś takiego. Zwykle twoje zatrzymania i wydania są w równowadze, więc nigdy nie musisz patrzeć na liczbę zatrzymań. To, co mówi ten fragment kodu, brzmi: "Nie wiem, kto mnie zatrzymał i zapomniałem go uwolnić, chcę tylko, aby moja pamięć została uwolniona, nie obchodzi mnie to, że inne odniesienia będą teraz zwisały". To nie będzie się kompilować z ARC (co dziwne, przejście na ARC może po prostu naprawić błąd, który autor próbował obejść).

2

Znaczenie kodu polega na zmuszeniu obiektu do zwolnienia w tej chwili, bez względu na przyszłe konsekwencje. (I będą konsekwencje!)

Kod jest fatalnie wadliwy, ponieważ nie uwzględnia faktu, że ktoś inny faktycznie "posiada" ten obiekt. Innymi słowy, coś "przydzielonego" temu obiektowi i dowolnej liczbie innych rzeczy mogło "zachować" ten obiekt (może strukturę danych, taką jak NSArray, może pulę autorelease, może jakiś kod na ramce stosu, która po prostu robi "zatrzymanie"); wszystkie te rzeczy są współwłasnością tego obiektu. Jeśli obiekt popełnia samobójstwo (co robi narzędzie releaseMySelf), ci "właściciele" nagle wskazują na złą pamięć, a to prowadzi do nieoczekiwanego zachowania.

Mam nadzieję, że napisany kod po prostu się zawiesi. Być może oryginalny autor uniknął tych wypadków przez wyciekanie pamięci gdzie indziej.

+1

Wielkie dzięki! To jest to, co myślę, to jest okropny kod. – Ricardo

7

Ponieważ wszystkie odpowiedzi wydają się błędnie rozumieć mójRetainCount jako [self retainCount], pozwól mi podać powód, dla którego ten kod mógł zostać napisany: Może to być, że ten kod w jakiś sposób uruchamia wątki lub w inny sposób, że klienci rejestrują się w nim, i że myRetainCount to efektywnie liczba tych klientów, niezależnie od rzeczywistej liczby zatrzymań systemu operacyjnego. Jednak każdy z klientów może również zachować własne zachowanie w stylu ObjC.

Ta funkcja może być wywoływana w przypadku, gdy żądanie zostało przerwane i może po prostu usunąć wszystkich klientów jednocześnie, a następnie wykonać wszystkie wydania. Nie jest to dobry projekt, ale jeśli tak działa kod (i nie pominąłeś int myRetainCount = [self retainCount] lub przesłonięcia retain/release) przynajmniej nie musi to być buggy.

Jest to jednak najprawdopodobniej zły podział obowiązków lub kludgey i hackneyed próba uniknięcia zachować kręgi bez naprawdę poprawy niczego.

+0

Jednak nadal nie jest to właściwy sposób implementacji - nadal autor nie otrzymuje poprawnego zarządzania pamięcią - zachowaj/zwolnij itp. Działa z wątkami – Mark

+0

Wątki były po prostu przykładami klientów, które zwykle rejestruje się/wyrejestruje, np. dla pul wątków lub zachowania "anuluj wszystko". – uliwitness