estoy tratando de escribir esto forma más concisa posible, pero no es fácil de describir - así que gracias por la lectura =)Bloques del objetivo C: ¿Hay alguna forma de evitar que se retenga el "yo"?
Soy el principal desarrollador del Marco iPhone Open Source Sparrow. Sparrow se basa en la biblioteca Flash AS3 y, por lo tanto, tiene un sistema de eventos como AS3. Actualmente, ese sistema funciona al especificar selectores, pero me encantaría expandir ese sistema al permitir el uso de bloques para los oyentes de eventos. Sin embargo, estoy tropezando con problemas de gestión de memoria.
Le mostraré un caso de uso típico de eventos, tal como se manejan ahora.
// 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
}
Eso es bastante directo: cuando un objeto se agrega a la lista de visualización, recibe un evento. Actualmente, la clase base registra los oyentes de eventos en una matriz de NSInvocation-objects. La Invocación NS se crea de forma que no contiene y mantiene su objetivo y sus argumentos. (El usuario puede hacerlo, pero en el 99% de los casos, no es necesario).
El hecho de que no se conserven estos objetos fue una elección consciente: de lo contrario, el código anterior provocaría una pérdida de memoria, ¡incluso si el usuario eliminara el detector de eventos en el método dealloc! He aquí por qué:
- (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 primera vista, esto parece muy bien: la retienen en el método init-se empareja por una liberación en el método dealloc. Sin embargo, eso no funciona, ya que nunca se llamará al método dealloc, ¡porque el recuento de retenciones nunca llega a cero!
Como dije, el método 'addEventListener ...'- hace, por exactamente este motivo, no retener nada en su versión predeterminada. Debido a la forma en que funcionan los eventos (casi siempre son enviados por objetos "propios" o "secundarios", que se retienen de todos modos), eso no es un problema.
Sin embargo, y ahora llegamos a la parte central de la pregunta: No puedo hacer eso con bloques. Mira el bloque-variante de la gestión de eventos, como me gustaría que tenga:
- (id)init
{
if (self = [super init])
{
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[self startAnimations];
}];
}
return self;
}
Eso se ve muy bien y sería muy fácil de usar. Sin embargo: cuando el usuario llama a un método en 'self' o usa una variable miembro dentro del bloque, que será, bueno, casi siempre, el bloque se retendrá automáticamente 'self', y el objeto nunca será desasignado. .
Ahora, sé que cualquier usuario podría rectificar esta haciendo una referencia __block a uno mismo, de esta manera:
__block id blockSelf = self;
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[blockSelf startAnimations];
}];
Pero, honestamente, estoy seguro de que casi todos los usuarios no sabrían hacerlo u olvida hacerlo Una API no solo debe ser fácil de usar, sino también difícil de usar erróneamente, y esto claramente infringe ese principio. Los usuarios de la API definitivamente lo usarían indebidamente.
Lo que me molesta es que yo sé que 'yo' no tiene que ser retenido, funciona en mi implementación actual sin retenerlo. Así que I quiero decirle al bloque que no necesita retenerme, la biblioteca debe decirle al bloque eso, para que el usuario no tenga que pensar en ello.
En mi investigación, no he encontrado una manera de hacerlo.Y no puedo pensar en una forma de cambiar mi arquitectura para que se ajuste a esa limitación de bloques.
¿Alguien tiene una idea de lo que podría hacer al respecto?
Incluso si no lo has hecho, gracias por leer esto hasta ahora, sé que fue una pregunta prolija ;-)
Esta es una buena manera de hacerlo. Pero, para el registro, aliasing 'self' con una variable' __block' evitará la retención también. –