10

OK, così Apple ci ha portato ARC, il che è fantastico. Dopo aver refactoring la mia applicazione su ARC quasi tutto funziona correttamente ed è molto più semplice da sviluppare e mantenere.Conversione di oggetti a rilascio automatico su ARC

C'è solo un problema che ancora non riesco a capire.

Il mio programma di gestione del lavoro mostra informazioni dettagliate sui dettagli di proposte, ordini e così via nelle loro finestre. Così ho una classe speciale in cui WindowControllers ottiene allocati e avviate con initWithWindowNibName e poi la finestra è mostrato con ShowWindow:

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

Prima ARC l'istanza del WindowController ha fatto il rilascio come mostrato nella documentation:

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

Ma ora con ARC questo non è più possibile e cosa lo rende ancora peggio, nella mia classe speciale in cui è allocato e avviato WindowController, la stessa finestra viene rilasciata da ARC perché non c'è alcun puntatore alla finestraController.

La mia idea era quella di copiare il windowController in un array mutuable:

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

E nel metodo windowControllers delegato windowWillClose ho posto una notifica alla mia classe speciale:

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

Nella mia classe speciale Ascolto la notifica e rimuovo l'oggetto dall'array:

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

Funziona, ma non credo ancora che questo sia il modo corretto.

Qualcuno ha lo stesso problema o un consiglio per renderlo migliore?

+0

Il tuo nuovo metodo è fondamentalmente corretto. Con ARC, è necessario mantenere riferimenti espliciti agli oggetti affinché il compilatore sia in grado di tracciare correttamente le chiamate di mantenimento/rilascio. In realtà, penso che dovresti evitare trucchi come chiamare 'release' nei metodi di callback in generale, indipendentemente dal fatto che tu stia usando ARC. –

+1

@RobKeniger: cosa suggerisci invece? Immaginiamo di essere presenti nel delegato dell'app che inoltra le azioni per visualizzare/controllare i controller e di solito sono coinvolti molti controller diversi. Creare una proprietà per ognuno sembra essere inutile. –

risposta

10

Probabilmente utilizzerei un approccio delegato piuttosto che notifiche. Generalmente è meglio avere un oggetto esterno che tenga traccia delle finestre aperte. Oggetti autosufficienti, come il tuo vecchio sistema, rompono i punti fondamentali della proprietà dell'oggetto e rendono difficile trovare cose (come "dammi un elenco di finestre aperte").I non-singleton che stanno semplicemente "fluttuando" là fuori spesso tornano a morderti nella tua architettura (ho dovuto sistemarlo abbastanza spesso).

Detto questo, a volte l'auto-proprietà è almeno conveniente, e nel peggiore dei casi non-fine-del-mondo. Quindi self-own. L'unica differenza è che devi farlo esplicitamente anziché corrispondere a una perdita e una sovra-release (che è ciò che stava facendo il tuo vecchio codice).

Creare una proprietà privata strong. Assegna self ad esso. Ciò creerà un ciclo di conservazione che ti manterrà attivo finché non imposterai la proprietà su nil.

+0

Quindi la tua risposta è di mantenere una serie di sottoclassi di controller finestre nella classe che funge da delegato e viene chiamata nel metodo windowWillClose del controller della finestra? –

+3

A seconda del sistema, spesso ho solo un singleton 'WindowManager' centrale e gli faccio vedere' NSWindowWillCloseNotification', ma a volte uso la delega come dici tu. Dipende da come deve essere integrato. Il WindowManager possiede in genere tutti i WindowController nel sistema. Questo è solo il design che di solito trovo che finisca quando ho finito. –

+0

Rob: Dopo aver fatto molte ulteriori ricerche su questo argomento (in particolare attraverso il corso "iPad e iPhone App Development" di Paul Hegarty, trovato in iTunes U, che posso consigliare vivamente) accetto la tua ultima risposta. L'ho fatto in questo modo (guardando il 'NSWindowWillCloseNotification') e sembra essere il modo più elegante e ragionevole. Quindi grazie a tutti. – archibaldtuttle

0

Senza hack, non esiste un modo elegante per mantenere un oggetto conservato oltre ad avere un forte riferimento ad esso in qualche altro oggetto. Ad esempio, è possibile mantenere uno statico NSMutableArray/NSMutableSet, aggiungere il controller lì e rimuoverlo in windowsWillClose:. Questo sarà più breve di una notifica. Per rendere questo riutilizzabile, crea un singleton WindowControllerRegistry con un array, in cui aggiungi controller come questo, e che ascolterà automaticamente NSWindowWillCloseNotification e li rimuoverà dal suo array, rilasciando così la proprietà.

Come una soluzione rapida, è possibile eseguire retain/autorelease chiamate da 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

Grazie, hamstergene, probabilmente funzionerà. Ma sto cercando una soluzione un po 'più elegante. Deve esserci una soluzione senza usare retain e release. Altrimenti non dovevo passare ad ARC. – archibaldtuttle

+0

ARC consente di mantenere/rilasciare una funzione del compilatore. Chiamarli in questo modo è un comportamento del compilatore indefinito sotto ARC e potrebbe fare qualsiasi cosa. Potrebbe perdere, funzionare, bloccarsi, a volte o sempre. Secondo le specifiche, potrebbe venire e dare un calcio al tuo cane. Il compilatore ha ottimizzazioni che rimuovono ritegni e rilasci non necessari come meglio crede. A volte ARC "falsa" conserva e rilascia. Il tuo approccio può sbilanciare il sistema a seconda di come ottimizza il compilatore. Maggiori informazioni sul comportamento non definito: http://blog.regehr.org/archives/213 Ulteriori informazioni sulle ottimizzazioni ARC: http://bit.ly/friday-qa-2011-09-30 –

+0

@RobNapier Il trucco è sporco e non dovrebbe essere usato, ma perché pensi che abbia un comportamento indefinito? ARC non annulla la spedizione dinamica. – hamstergene

0

Penso che il vostro approccio alternativo dovrebbe essere corretto, ma non credo che avete bisogno della seconda notifica. Si dovrebbe essere in grado di fare:

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

Supponendo che il "proposalWindowArray" è una NSMutableArray statica.

+0

Sfortunatamente propostaWindowArray è una proprietà privata della mia classe speciale detailWindowController che gestisce l'allocazione e l'inizializzazione della finestraController come propostaWindowController. Quindi non c'è modo di ottenere l'accesso alla proprietà privata di detailWindowController fuori dalla propostaWindowController. – archibaldtuttle

0

Ho avuto questo stesso problema quando sono passato a ARC. La tua soluzione funziona, ma lo stai rendendo troppo complicato. Puoi essenzialmente fare ciò che stavi facendo in precedenza facendo sì che la finestra si rilasciasse quando si chiude, ma in un modo compatibile con ARC.

La soluzione è semplicemente creare una proprietà della classe all'interno della classe stessa. Per il vostro esempio, in DetailWindowController, è necessario aggiungere la seguente proprietà:

@property (strong) DetailWindowController  *theWindowController; 

Poi, quando si crea la finestra con il codice di cui sopra, aggiungere una riga in questo modo:

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

Poi finalmente, di avere ARC rilascia la finestra quando è chiusa come hai fatto con la pre-ARC autorelease, nella classe DetailWindowController, fai semplicemente:

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