NSNotificación observada por objects liberados

Consulte la Actualización a continuación … aunque inicialmente esto parecía ser un problema de animation, resultó que era un problema con las notifications. Tenga cuidado: se observará una NSNotification incluso por objects que haya descartado. Asegúrate de removeObserver: para evitar esto.

He estado usando la animation basada en bloques por primera vez y me he encontrado con una situación en la que el bloque de "finalización" parece recibirse más de una vez por una activación de un bloque de "animaciones". No puedo entender por qué esto sucedería, pero parece estar sucediendo con cierta consistencia después de que mi juego se desarrolle por un time. Aquí está el código en cuestión …

 - (void)player:(Player *)player takeStep:(NSInteger)step onWalk:(NSArray *)walk { if (step < walk.count) { // take this step NSLog(@"taking step %i", step); NTTileView *tile = [walk objectAtIndex:step]; [UIView animateWithDuration:0.5 animations:^{ NSLog(@"animating step to tile %@",tile); [player.pawn moveToTile:tile]; if ([[NSUserDefaults standardUserDefaults] boolForKey:@"UseAudio"]) [boombox play]; } completion:^(BOOL finished){ if (finished) { NSLog(@"step %i done, moving on",step); [self player:player takeStep:step+1 onWalk:walk]; } else { NSLog(@"step %i unfinished, jumping to end",step); NTTileView *lastTile = [walk lastObject]; [player.pawn setCenter:lastTile.center]; } }]; } } 

Este es un método recursivo que se inicia inicialmente con el paso = 1 y una matriz de "fichas" para que el peón del jugador pueda animar. Una vez que se completa cada paso, el método se denomina recursivamente para dar el siguiente paso. Cuando la matriz se queda sin mosaicos, el trabajo se realiza. En el bloque completado, tomamos el siguiente paso o (si la animation no se terminó) basta con saltar al último mosaico. Aquí hay un logging de una ejecución …

 1...[79719:1be03] taking step 1 2...[79719:1be03] animating step to tile <NTTileView: 0x957cd30; baseClass = UIControl; frame = (273 260; 57 54); layer = <CALayer: 0x957cc90>>; id = 31; type = TileTypeMethods; isHub = 0; isEndSpot = 0 3...[79719:1be03] step 1 done, moving on 4...[79719:1be03] taking step 2 5...[79719:1be03] animating step to tile <NTTileView: 0x957d3b0; baseClass = UIControl; frame = (268 202; 60 59); layer = <CALayer: 0x957d2e0>>; id = 30; type = TileTypeTermsAndTheory; isHub = 0; isEndSpot = 0 6...[79719:1be03] step 1 unfinished, jumping to end 7...[79719:1be03] step 2 done, moving on 8...[79719:1be03] taking step 3 9...[79719:1be03] animating step to tile <NTTileView: 0x957dbc0; baseClass = UIControl; frame = (268 139; 59 64); layer = <CALayer: 0x957db40>>; id = 29; type = TileTypePeople; isHub = 0; isEndSpot = 0 10...[79719:1be03] step 3 done, moving on 

Observe que después de iniciar el segundo paso en la línea 4, el logging informa tanto una finalización "no terminada" del primer paso en la línea 6 y luego un segundo paso finalizado en la línea 7. Sin embargo, el paso 1 se completó en la línea 3. ¿Cómo puede ser completado en la línea 3 y la línea 6? En este caso, uno se completó con finished = YES y el otro con finished = NO, pero también he visto esta ejecución con dos o más terminadas = YES líneas registradas para un solo paso.

¿Qué causaría algo como esto? Ni siquiera estoy seguro de dónde comenzar a search el error, o tal vez no entiendo la naturaleza del bloque de finalización para la animation de iOS.

Lo extraño es que este código funciona perfectamente para un "juego", pero tan pronto como la aplicación comienza un nuevo "juego" en la misma ejecución, el código comienza a producir más éxitos de "finalización", mientras más juegos comienzo, más Hechizos de "finalización" que recibo por animation. Parece que la basura sobrante de los viejos juegos interfiere, pero no hay fugas visibles en el analizador estático. Estoy desconcertado

Actualización y solución

Este problema se debió al hecho de que los viejos tableros descartados seguían escuchando notifications para mover los peones. A pesar de que se lanzaron los tablones antiguos, no eliminaron específicamente las requestes de notificación cuando finalizaron los juegos. Dado que el sistema no descarta inmediatamente los objects liberados, esos tableros "fantasmas" seguían escuchando la notificación global para mover los peones. Y a pesar de que estaban "muertos" para nosotros, ¡estaban lo suficientemente vivos como para responder y luchar por la animation de los peones!

La solución era hacer que cada placa [[NSNotificationCenter defaultCenter] removeObserver:self] antes de lanzarlo.

Ver la actualización de la pregunta. Este problema terminó siendo acerca de las notifications que llamaron por primera vez a los peones para moverse, a los que llamamos desde copys antiguas publicadas de nuestra vista de la placa. ¡Cuidado, las notifications sobreviven al lanzamiento de objects, asegúrese de eliminar explícitamente las requestes de notificación cuando termine con los objects!

No estoy seguro, pero creo que esto se debe al hecho de que estás cambiando la position dentro del mismo runloop mientras la animation está terminando, por lo tanto, interrumpiendo la primera animation. Si hiciste esto:

 [self performSelector:@selector(takeStep:) withObject:[object that describes step] afterDelay:0.0]; 

Creo que arreglaría tu problema.