sto cercando di scrivere questo nel modo più conciso possibile, ma non è facile descrivere - quindi grazie per la lettura =)Blocchi oggettivi C: esiste un modo per evitare che il "sé" venga mantenuto?
Sono lo sviluppatore principale di iPhone Open Source quadro Sparrow. Sparrow è modellato dopo la libreria Flash AS3, e quindi ha un sistema di eventi proprio come AS3. Attualmente, quel sistema funziona specificando selettori - ma mi piacerebbe espandere quel sistema consentendo l'uso di blocchi per gli ascoltatori di eventi. Tuttavia, sto inciampando su problemi di gestione della memoria.
Ti mostrerò un tipico caso d'uso di eventi, man mano che vengono gestiti.
// init-method of a display object, inheriting from
// the base event dispatcher class
- (id)init
{
if (self = [super init])
{
// the method 'addEventListener...' is defined in the base class
[self addEventListener:@selector(onAddedToStage:)
atObject:self
forType:SP_EVENT_TYPE_ADDED_TO_STAGE];
}
return self;
}
// the corresponding event listener
- (void)onAddedToStage:(SPEvent *)event
{
[self startAnimations]; // call some method of self
}
Questo è abbastanza semplice: quando un oggetto viene aggiunto all'elenco di visualizzazione, riceve un evento. Attualmente, la classe base registra i listener di eventi in una matrice di oggetti NSInvocation. NSInvocation viene creato in modo tale che lo non mantenga il proprio target e gli argomenti non. (L'utente può farlo, ma nel 99% dei casi non è necessario).
Che questi oggetti non vengano conservati è stata una scelta consapevole: altrimenti, il codice precedente causerebbe un porro di memoria, anche se l'utente ha rimosso il listener di eventi nel metodo dealloc! Ecco perché:
- (id)init
{
if (self = [super init])
{
// [self addEventListener: ...] would somehow cause:
[self retain]; // (A)
}
return self;
}
// the corresponding event listener
- (void)dealloc
{
// [self removeEventListener...] would cause:
[self release]; // (B)
[super dealloc];
}
A prima vista, che sembra bene: la conservano nel init metodo è abbinato da un rilascio nel metodo dealloc. Tuttavia, ciò non funziona, poiché il metodo dealloc non verrà mai chiamato, perché il conteggio dei fermi non raggiunge mai lo zero!
Come ho già detto, il metodo 'addEventListener ...', proprio per questo motivo, non conserva nulla nella sua versione predefinita. A causa del modo in cui gli eventi funzionano (vengono quasi sempre inviati da "sé" o oggetti figlio, che vengono comunque mantenuti), non è un problema.
Tuttavia, e ora veniamo alla parte centrale della domanda: non posso farlo con i blocchi. Guardate il blocco-variante di gestione degli eventi, come vorrei che avesse:
- (id)init
{
if (self = [super init])
{
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[self startAnimations];
}];
}
return self;
}
che sembra grande e sarebbe molto facile da usare. Tuttavia: quando l'utente chiama un metodo su 'self' o usa una variabile membro all'interno del blocco - che sarà, beh, quasi sempre - il blocco manterrà automaticamente 'self', e l'oggetto non sarà mai deallocato .
Ora, so che qualsiasi utente potrebbe ovviare a tale situazione, facendo un riferimento __block a sé, in questo modo:
__block id blockSelf = self;
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[blockSelf startAnimations];
}];
Ma, onestamente, sono sicuro che quasi tutti gli utenti non saprebbe farlo o dimentica di farlo. Un'API dovrebbe essere non solo facile da usare, ma anche difficile da utilizzare in modo errato e questo chiaramente viola tale principio. Gli utenti dell'API lo abuserebbero sicuramente.
Quello che mi infastidisce è che io conosco che 'auto' non deve essere conservato - funziona nella mia attuale implementazione senza conservarlo. Quindi, I voglio dire al blocco che non ha bisogno di conservare se stesso, la libreria, dovrebbe dire al blocco che, in modo che l'utente non debba pensarci.
Nella mia ricerca, non ho trovato il modo di farlo.E non riesco a pensare a un modo per cambiare la mia architettura per adattarla a quella limitazione di blocchi.
Qualcuno ha avuto un'idea di cosa avrei potuto fare al riguardo?
Anche se non lo hai, grazie per aver letto fino a qui - so che è stata una domanda prolissa ;-)
Questo è un bel modo per farlo, ma, per la cronaca, l'aliasing 'self' con una variabile' __block' impedirà anche il mantenimento. –