2010-09-24 6 views
6

Tengo un problema con un objeto Objective-C (en una aplicación de juegos de iOS) que está misteriosamente desasignado.¿Por qué mi objeto Objective-C está desasignado?

El objeto es una instancia de GameCharacter que se crea una instancia de esta manera:

for (int c = 0; c < kNrOfGuards; c++) { 
    GameCharacter* guard = [[GameCharacter alloc] initGuard:self sprite:guardSprite]; 
    [characterArray addObject:guard]; 
    [guard release]; 
} 

También tengo un método de conveniencia para encontrar un GameCharacter:

- (GameCharacter*)findCharacterWithIndex:(int)index { 
    return [characterArray objectAtIndex:index]; 
} 

Y el código que genera el error parece :

for (int c = 0; c < [self characterCount]; c++) { 
    GameCharacter* tempCharacter = [self findCharacterWithIndex:c]; 
    if (tempCharacter.playerId == playerIndex]) { 
     ... 
    } 
} 

Ejecutando este código para som tiempo e (nunca inmediatamente) genera un error en la consola:

[GameCharacter playerId]: mensaje enviado a la instancia desasignado 0x4e47560

Con la NSZombieEnabled trick He conseguido localizar el objeto (s) Eso está causando el problema, pero todavía no puedo entender por qué este objeto está siendo desasignado. Buscar en mi código para "release"/"dealloc" no produce ninguna pista.

He intentado eliminar el "lanzamiento" (e incluso agregar un "retener") al ciclo alloc/init (ver arriba), parece extender el tiempo de ejecución de la aplicación pero no eliminar completamente el problema .

¡Cualquier sugerencia sería muy apreciada!

EDITAR

Gracias a quixoto, Olie, Eiko, tc., He descubierto que es mi objeto GameCharacter que se cancela la asignación, pero yo todavía no entiendo muy bien por qué. Aquí está el registro de seguimiento en orden cronológico inverso:

#0 -[GameCharacter dealloc] 
#1 objc_setProperty 
#2 -[TiledGroundLayer setSelectedCharacter:] 
#3 -[TiledGroundLayer selectNextCharacterForPlayer:searchStep:] 
#4 -[GameScene selectNextCharacter:] 
#5 -[GameScene endTurn] 
#6 -[HUDLayer onClickDone:] 

¿Qué ocurre aquí, es que el usuario hace clic en "Done", se cambia el carácter seleccionado en la pantalla y por lo tanto la propiedad selectedCharacter en TiledGroundLayer (paso # 2-4) Como selectedCharacter posee el objeto GameCharacter anterior, parece que se está desasignando. ¿Pero por qué no está siendo retenido correctamente por NSMutableArray ([characterArray addObject:guard];)?

+3

¿Su variable characterArray fue creada como '[NSMutableArray array]' o '[[NSMutableArray init] alloc]'? – tidwall

+0

@Tom: ¿en qué clases viven estos fragmentos de código? Parece que tienes un código en otro lugar que interactúa con el characterArray que da como resultado el dealloc. Podría asociarse con la eliminación del objeto de la matriz, por ejemplo, ¿eso sucede? –

+0

Miraría cuidadosamente characterArray y cuando se desasignara. Como tiene una función de conveniencia para devolver un guardia, podría sostenerlo fácilmente después de que characterArray y todos sus contenidos se desasignaran.Si no tiene el método de conveniencia (y no puedo ver su beneficio aquí), en lugar de eso, puede simplemente tomar el protector de la matriz cada vez que lo necesite y estar más seguro de que characterArray aún era necesario y válido en ese punto. –

Respuesta

1

Basado en su actualización

#0 -[GameCharacter dealloc] 
#1 objc_setProperty 
#2 -[TiledGroundLayer setSelectedCharacter:] 

yo supongo que usted está liberando su referencia existente a un objeto en su organismo, seguido de retener la nueva copia. Sin embargo, si el objeto nuevo es exactamente el mismo objeto que la referencia existente, es posible que esté enviando el mensaje retain a un objeto ya desasignado.

-(void) setSelectedCharacter: (GameCharacter*) newCharacter 
{ 
    [character release]; // Oops if character == newCharacter 
    character = [newCharacter retain]; 
} 
+0

La propiedad se define como: '@property (no atómico, retener) GameCharacter * selectedCharacter;' - ¿la parte "retener" no se ocuparía de esto? –

+0

Paul Alexander, ¡tienes toda la razón! Era la parte "retener" de '@property (nonatomic, ** retain **)' que liberaba el objeto prematuramente. Al cambiarlo a "asignar" en su lugar, el problema se resuelve. ¡Gracias a todos ustedes! –

+1

Para que quede claro, cambiarlo para asignar (retener) cambia el comportamiento del código. Es posible que haya solucionado ESTE error, pero tenga cuidado de no haber introducido una docena más. – Olie

0

Lanzas la instancia de GameCharacter en algún lugar. Tu código se ve bien, por lo que está en algún lugar de los otros lugares que usa esos objetos.

1

Depuración espuria conserva/comunicados en 3 pasos sencillos:.

  1. Override -retain, -release y -autorelease para la clase en la que está interesado en Hacer que se registrará un mensaje (NSLog(@"%@ %s", self, sel_getName(_cmd))) y super -call.
  2. Breakpoint todos estos métodos (en el super -call, es decir, después del mensaje de registro para saber qué objeto es). Edite el punto de interrupción; agregue el comando "bt" y marque la casilla de auto-continuar (o simplemente use dos comandos "bt", "continuar").
  3. Borrar el registro. Ejecuta la aplicación. Imprime el registro. Péguelo en una pizarra blanca.Dibuje algunas flechas hasta que encuentre el falso release/autorelease.

Mi primera impresión fue que characterArray se estaba lanzando demasiado pronto, pero eso debería provocar que se queje de enviar un mensaje a un NSArray desasignado. A menos que, por supuesto, esté accediendo al characterArray desde múltiples hilos (¡no haga eso!).

2

No hay suficiente código aquí para decir cuál es el problema pero, a partir del mensaje de error, supongo que el objeto playerId es lo que no se conserva. Es decir, parece que tu tempCharacter está bien, pero no el campo playerId.

Si tiene

@property(nonatomic,retain) SomeObject *playerId; 

a continuación, recordar que

playerId = foo; 

se NO sostener sobre el objeto para usted. Debe utilizar el descriptor de acceso:

self.playerId = foo; 

EDITAR en respuesta a la pregunta de edición de Tom:

absolutamente, te garantizo positivamente que los objetos colocados en una NSMutableArray son retenidos por esa matriz hasta que (a) se eliminan o (b) se libera la matriz. Para que pueda dejar de buscar allí, el problema está en otro lugar. :)

Una cosa que puede probar es añadir el siguiente código al objeto que está siendo puesto en libertad cuando se cree que no debería:

#pragma mark - 
#pragma mark Memory-use debugging 

#define DEBUG_RETAIN_RELEASE 0 
#define DEBUG_ALLOC_DEALLOC  0 



#if DEBUG_ALLOC_DEALLOC 

static int allocCounter = 0; 
+(id)alloc 
{ 
    id me = [super alloc]; 
    NSLog(@"%@ ALLOC (%2d): %@", [me class], ++allocCounter, me); 

    return me; 
} 

#endif 


#if DEBUG_RETAIN_RELEASE 
- (id)retain 
{ 
    id result = [super retain]; 
    NSLog(@"%@ retain  %@, count: %2d", [self class], self, [self retainCount]); 
    return result; 
} 


- (void)release 
{ 
    // we have to log BEFORE the release, in case it's the last one! e 
    NSLog(@"%@ RELEASE  %@, count: %2d", [self class], self, ([self retainCount] - 1)); 
    [super release]; 
} 


- (id)autorelease 
{ 
    id result = [super autorelease]; 
    NSLog(@"%@ AUTOrelease %@, count: %2d", [self class], self, [self retainCount]); 
    return result; 
} 

#endif 

// ... 


- (void)dealloc 
{ 
#if DEBUG_ALLOC_DEALLOC 
    NSLog(@"%@ dealloc (%2d): %@", [self class], --allocCounter, self); 
#endif 

    [self releaseMyStuff]; 
    [super dealloc]; 
} 

A continuación, empezar con DEBUG_ALLOC_DEALLOC = 1 y poner un punto de interrupción en la declaración de registro dealloc. Si eso no ayuda, establezca DEBUG_RETAIN_RELEASE = 1 y rompa ambos en la versión &.

Se sorprenderá de todo lo que iOS conserva, pero no se preocupe, iOS promete un relanzamiento equilibrado si se usa correctamente. Solo te estoy advirtiendo porque puedes esperar un conteo de retención mucho más bajo, y te sorprendería verlo subir durante una operación u otra.

Suerte!

Cuestiones relacionadas