10

OK, więc Apple wprowadził do nas ARC, co jest świetne. Po refakturowaniu mojej aplikacji do ARC prawie wszystko działa dobrze i teraz jest o wiele łatwiej opracowywać i utrzymywać.Konwertowanie obiektów zwalniających do ARC

Jest tylko jeden problem, którego wciąż nie mogę rozgryźć.

Mój program zarządzania zadaniami wyświetla różne informacje o propozycjach, zamówieniach itd. W ich własnych oknach. Więc mam specjalną klasę gdzie WindowControllers zostaje przydzielony i zainicjowane z initWithWindowNibName a następnie okno jest pokazany z ShowWindow:

DetailWindowController *proposalWindowController = [[DetailWindowController alloc] initWithWindowNibName:@"ThePorposalWindow"]; 
[proposalWindowController showWindow:nil]; 

Przed ARC instancję WindowController zrobił uwalnianie jak pokazano na documentation:

- (void)windowWillClose:(NSNotification *)notification 
{ 
    [self autorelease]; 
} 

Ale teraz z ARC nie jest to już możliwe i co jest jeszcze gorsze, w mojej specjalnej klasie, gdzie WindowController jest przydzielany i inicjowany, ten sam WindowController jest zwalniany przez ARC, ponieważ nie ma wskaźnika do windowController.

Mój pomysł był aby skopiować windowController się z mutuable tablicy:

[proposalWindowArray addObject:proposalWindowController]; 
[[proposalWindowArray lastObject] showWindow:nil]; 

A w metodzie windowControllers delegata windowWillClose mogę wysłać powiadomienie do mojego specjalnego Klasa:

- (void)windowWillClose:(NSNotification *)notification 
{ 
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ProposalWindowWillClose" object:[[self window] windowController] userInfo:nil]; 
} 

W mojej klasie specjalnej Wysłuchuję powiadomienia i usuwam obiekt z tablicy:

- (void) proposalWindowWasClosed: (NSNotification *) notification 
{ 
    [proposalWindowArray removeObjectIdenticalTo:[notification object]]; 
} 

Działa, ale nadal nie wierzę, że to jest poprawna metoda.

Czy ktoś ma ten sam problem lub napiwek, aby go poprawić?

+0

Twoja nowa metoda jest zasadniczo poprawna. W przypadku ARC należy zachować wyraźne odwołania do obiektów, aby kompilator mógł poprawnie śledzić wywołania funkcji keep/release. W rzeczywistości uważam, że powinieneś unikać takich sztuczek jak nazywanie 'release' w ogóle metodami wywołania zwrotnego, niezależnie od tego, czy używasz ARC. –

+1

@RobKeniger: Co proponujesz zamiast tego? Wyobraźmy sobie, że znajdujemy się w delegacie aplikacji, który przekazuje otrzymane akcje do przeglądania/kontrolerów okien i zazwyczaj jest zaangażowanych wiele różnych kontrolerów. Tworzenie właściwości dla każdej z nich wydaje się niepotrzebne. –

Odpowiedz

10

Prawdopodobnie użyłbym raczej podejścia delegowanego niż powiadomienia. Generalnie lepiej jest mieć zewnętrzny obiekt, który śledzi otwarte okna. Samozachowujące się obiekty, takie jak stary system, łamią podstawowe punkty własności obiektu i utrudniają znajdowanie rzeczy (np. "Podaj listę otwartych okien").Non-Singleton, które są po prostu "pływające" często powracają, by cię ugryźć w twoją architekturę (musiałem to często naprawiać).

To powiedziawszy, czasami samoposiadanie jest co najmniej wygodne, aw najgorszym przypadku nie-na-końcu świata. Tak więc jesteś samowystarczalny. Jedyna różnica polega na tym, że musisz to zrobić jawnie, zamiast dopasowywać wyciek i nadpisywanie (co robił twój stary kod).

Utwórz prywatną właściwość strong. Przypisz do niego self. To utworzy pętlę zatrzymującą, która będzie Cię utrzymywać, dopóki nie ustawisz właściwości na nil.

+0

Twoja odpowiedź brzmi: zachowaj w klasie tablicę podklas kontrolera okien, która działa jako delegat i jest wywoływana w metodzie windowWillClose kontrolera okienkowego? –

+3

W zależności od systemu, często mam centralny singleton 'WindowManager' i pozwalam mu oglądać' NSWindowWillCloseNotification', ale czasami używam delegacji, jak mówisz. Zależy od tego, jak powinna być zintegrowana. WindowManager zazwyczaj posiada wszystkie WindowControllery w systemie. Po prostu skończę z tym projektem, który zwykle znajduję. –

+0

Rob: Po przeprowadzeniu wielu dalszych badań na ten temat (szczególnie w ramach kursu "Rozwój aplikacji na iPada i iPhone'a" od Paula Hegarty'ego, znalezionego w iTunes U, który mogę gorąco polecić) przyjmuję twoją ostatnią odpowiedź. Zrobiłem to teraz sposób (oglądając "NSWindowWillCloseNotification") i wydaje się, że jest to najbardziej elegancki i rozsądny sposób. Dziękuję wszystkim. – archibaldtuttle

0

Bez hacków nie ma eleganckiego sposobu na zatrzymanie obiektu poza silnym odniesieniem do niego w jakimś innym obiekcie. Możesz na przykład zachować statyczną wartość NSMutableArray/NSMutableSet, dodać tam swój kontroler i usunąć go w windowsWillClose:. To będzie krótsze niż wysyłanie powiadomienia. Aby było to możliwe do ponownego użycia, utwórz singleton WindowControllerRegistry z tablicą, do której dodajesz kontrolery, takie jak ten, i który automatycznie nasłuchuje NSWindowWillCloseNotification i usunie je z tablicy, zwalniając własność.

Jako szybki obejść ten problem, można wykonać retain/autorelease połączeń z non-ARC file:

my_retain(self); 
my_autorelease(self); 

// ArcDisabled.mm 
void my_retain(id obj) { [obj retain]; } 
void my_autorelease(id obj) { [obj autorelease]; } 
+0

Dziękuję ci hamstergene, to prawdopodobnie zadziała. Ale szukam nieco bardziej eleganckiego rozwiązania. Musi istnieć rozwiązanie bez użycia funkcji zatrzymywania i zwalniania. W przeciwnym razie nie musiałem przełączać się na ARC. – archibaldtuttle

+0

ARC powoduje zachowanie/zwolnienie funkcji kompilatora. Wywoływanie ich w ten sposób jest niezdefiniowane zachowanie kompilatora pod ARC i może zrobić wszystko. Może przeciekać, pracować, zderzać się, czasami lub zawsze. Specyfikacja może przyjść i kopnąć twojego psa. Kompilator ma optymalizacje, które usuwają niepotrzebne zachowania i zwalniają zgodnie z oczekiwaniami. ARC czasami "podróbki" zachowują i zwalniają. Twoje podejście może zaburzyć równowagę systemu w zależności od optymalizacji kompilatora. Więcej o niezdefiniowanym zachowaniu: http://blog.regehr.org/archives/213 Więcej informacji na temat optymalizacji ARC: http://bit.ly/friday-qa-2011-09-30 –

+0

@RobNapier Sztuczka jest brudna i nie powinna być użytym, ale dlaczego uważasz, że ma niezdefiniowane zachowanie? ARC nie anuluje wysyłki dynamicznej. – hamstergene

0

myślę, że alternatywne podejście powinno być poprawne, ale nie sądzę, trzeba drugie powiadomienie. Powinieneś być w stanie to zrobić:

- (void)windowWillClose:(NSNotification *)notification 
{ 
    [proposalWindowArray removeObjectIdenticalTo:self]; 
} 

Zakładając "proposalWindowArray" jest statycznym NSMutableArray.

+0

Niestety proposalWindowArray jest prywatną własnością mojej specjalnej klasy detailWindowController, która zarządza alokowaniem i inicjowaniem kontrolek windowPontroller takich jak proposalWindowController. Tak więc nie ma sposobu na uzyskanie dostępu do prywatnej własności detailWindowController z wnioskuWindowController. – archibaldtuttle

0

Miałem ten sam problem, kiedy przestawiłem się na ARC. Twoje rozwiązanie działa, ale robisz to zbyt skomplikowane. Zasadniczo możesz zrobić to, co robiłeś wcześniej, przez samo otwarcie okna po zamknięciu, ale w sposób zgodny z ARC.

Rozwiązaniem jest proste utworzenie właściwości klasy w samej klasie. Na swoim przykładzie, w DetailWindowController, należy dodać następującą właściwość:

@property (strong) DetailWindowController  *theWindowController; 

Następnie podczas tworzenia okno z kodem powyżej, dodać jedną linię tak:

DetailWindowController *proposalWindowController = [[DetailWindowController alloc] initWithWindowNibName:@"ThePorposalWindow"]; 
[preferenceController setTheWindowController:proposalWindowController]; 
[proposalWindowController showWindow:nil]; 

Wtedy wreszcie mieć ARC zwalnia okno, gdy jest ono zamknięte, podobnie jak w przypadku autorelease przed ARC, w klasie DetailWindowController, po prostu wykonaj:

- (void)windowWillClose:(NSNotification *)notification 
{ 
    // Let ARC tear this down and clean it up 
    [self setTheWindowController:nil]; 
} 
Powiązane problemy