2012-07-25 6 views
5

Ho incontrato questo problema un paio di volte e voglio conoscere l'approccio corretto da prendere.Impedire ad ARC di rilasciare qualcosa che non è attualmente in uso ma lo sarà presto?

Per un esempio, diciamo che sto scrivendo un'app per iPhone e desidero una classe di visualizzazione di avviso personalizzata che utilizza blocchi.

Così ho scrivere la classe, poi in seguito nel mio codice vado:

MyAlertView *alert = [MyAlertView alertWithBlahBlahBlah...]; 
[alert addButton:@"button" withBlock:^{ ... }]; 
[alert show]; 

Da qualche parte nella classe di visualizzazione avviso, abbiamo

- (void)addButton:(NSString *)button withBlock:(void (^))block { 
    [_blocks setObject:[block copy] forKey:button]; 
} 

- (void)show { 
    ... drawing stuff ... 
    UIButton *button = ... 
    [button addTarget:self selector:@selector(buttonPressed:) ...]; 
    ... 
} 

- (void)buttonPressed:(id)sender { 
    ((void (^)())[_blocks objectForKey:[sender title]])(); 
} 

Quindi, la vista avviso ora si presenta va bene. Il problema è che, se tocco un pulsante, tenta di inviare il selettore buttonPressed: all'oggetto MyAlertView visualizzato. Il dispositivo MyAlertView è stato tuttavia rimosso dal superview in questo momento. ARC decide che poiché la vista di avviso non è più di proprietà di nessuno, dovrebbe essere deallocata, non sapendo che un pulsante ha bisogno di segnalarlo in futuro. Ciò provoca un arresto anomalo quando si tocca il pulsante.

Qual è il modo corretto per mantenere la vista di avviso in memoria? Potrei rendere l'oggetto MyAlertView una proprietà della classe che lo sta usando, ma questo è un po 'sciocco (cosa succede se voglio mostrare due avvisi contemporaneamente?).

+1

Perché è stato rimosso dalla sua superview se è ancora sullo schermo? –

+3

Se non si ha un riferimento ad esso da qualche parte, allora non si sarebbe in grado di deallocarlo quando si vuole "ad un certo punto nel futuro", quindi è necessario mantenere ** qualche ** tipo di riferimento a esso. Questo potrebbe essere in una proprietà, come suggerisci tu, o forse una serie di oggetti "memorizzati nella cache" di cui sarai in grado di disporre quando la tua classe viene deallocata. – lnafziger

+0

@JoshCaswell Questo è principalmente un esempio forzato. Stavo facendo qualcosa di simile anche se l'azione sarebbe avvenuta dopo che la vista fosse stata rimossa dallo schermo. –

risposta

7

Se un oggetto dovesse rimanere in memoria e non si dispone di un riferimento ad esso, questo è noto come una perdita di memoria. Come ho detto nei miei commenti, è necessario mantenere un po 'di riferimento in modo che a) non sia deallocato, b) si possa inviare un messaggio ad esso, e c) si può deallocarlo prima che la classe venga rilasciata .

Il modo più ovvio per farlo è con una proprietà della classe. Dato che hai detto che non vuoi farlo (forse hai un lotto), allora un'altra soluzione possibile sarebbe quella di mantenere una serie di oggetti memorizzati nella cache che prevedi di riutilizzare e eventualmente deallocare.

+0

Io vado avanti e segnalo come la risposta giusta perché chiaramente questo non è un problema con ARC, è un problema con me nella gestione dei miei riferimenti. Dovrò provare a implementare quell'idea di caching, che sembra l'approccio più pulito. –

1

Penso che sia possibile utilizzare performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay per mantenere la vista di avviso in runloop.

In realtà, ho appena trovato un'implementazione wrapper per UIAlertView utilizzando questa abilità.

Verificare UIAlertView input wrapper per ulteriori dettagli.

+0

Questo effettivamente risolve abbastanza bene il mio problema. L'unico cavillo che ho con esso è che si sente troppo hacky per inserire il codice di produzione; se per qualche ragione il mio utente lasciasse il proprio telefono con la vista di avviso aperta per alcune ore, potrebbero accadere cose strane. Immagino che se il timeout ha appena generato un altro timer e continuasse ad andare per sempre, questa potrebbe essere una buona soluzione? –

1

Molto semplicemente, stai infrangendo le regole di gestione della memoria. ARC non modifica le regole, ma le automatizza. Se hai bisogno di un oggetto per rimanere vivo, deve avere un proprietario. Ogni oggetto nel grafico dell'oggetto dell'app, fino al delegato dell'applicazione, ha un proprietario. Potrebbe non essere ovvio cosa sia quel proprietario (ea volte il proprietario potrebbe essere un pool di autorelease), ma ce n'è uno.

Se si desidera mantenere questa vista, è necessario che sia posseduta da qualcosa, anche se non è "attualmente in uso". Se è a schermo, dovrebbe essere parte della gerarchia della vista. In caso contrario, è probabile che il proprietario ideale sia l'oggetto che lo ha creato.

Problemi correlati