EXC_BAD_ACCESS al usar el auto "liberado" en dispatch_async

Tengo un juego escrito con el nuevo SpriteKit en iOS7. Tengo un SKSpriteNode personalizado que obtendría y mostraría una image de perfil de Facebook. Sin embargo, ya que puede demorar un poco en cargar la image. Intenté cargar la image en segundo plano cuando inicialicé el nodo y lo muestro solo cuando se carga la image. Aquí está el fragment de código que escribí:

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Code to load Facebook Profile picture // ... SKSpriteNode *fbFrame = [SKSpriteNode spriteNodeWithTexture:facebookPicTexture]; dispatch_async(dispatch_get_main_queue(), ^{ // self is my customised SKSpriteNode - so here I just add a sprite node of // a facebook picture to myself as a child [self addChild:fbFrame]; }); }); 

Funciona bien normalmente. Sin embargo, si la carga de la image del perfil de Facebook es lenta, es posible que el usuario ya haya cambiado a otra pantalla cuando se cargue la image. En tal caso, self se eliminará de la jerarquía de la escena y no se hará reference a ella.

Cuando leo el bloque doc, creo que el bloque asíncrono se mantendrá self y supongo que seguirá siendo válido cuando se llame al bloque principal de subprocesss. Resulta que de vez en cuando si la carga de la image es realmente lenta y la segunda dispatch_async se invoca cuando se elimina la [self addChild:fbFrame] de la jerarquía, se producirá un error de acceso incorrecto en la línea [self addChild:fbFrame] .

¿Entiendo que la gestión de la memory de bloque es incorrecta? ¿Y hay una manera de resolver ese tipo de problema?

Su comprensión de la gestión de la memory es correcta, que la presencia de self en este bloque se mantendrá hasta que se complete el bloque enviado. La solución es no conservar el self mientras se ejecuta el bloque, usando una reference weak a self dentro del bloque:

 __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // do some stuff in background here; when done, then: dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf addChild:fbFrame]; }); }); 

Debería revisar ese bloque para cualquier reference a self (ya sea explícita o implícitamente al hacer reference a un ivar directamente), y replacelos por weakSelf .

O bien, (improbable en este caso) a veces verá el patrón weakSelf / strongSelf en el que debe asegurarse de que no se libere el self durante la ejecución de ese bloque nested:

 __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // do some stuff in background here; when done, then: dispatch_async(dispatch_get_main_queue(), ^{ typeof(self) strongSelf = weakSelf; if (strongSelf) { // stuff that requires that if `self` existed at the start of this block, // that it won't be released during this block } }); }); 

Por cierto, el otro enfoque es cancelar la request de networking cuando se descarta el controller de vista. Esto requiere un cambio más significativo (usar un NSOperation basado en NSOperation en lugar de GCD, o usar el NSURLSessionTask basado en NSURLSessionTask que le permite cancelar la request), pero es la otra manera de abordar esto.

Creo que necesitas escribir así:

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul); objc_setAssociatedObject(self, @"yourTag", @"Alive", OBJC_ASSOCIATION_RETAIN); dispatch_async(queue, ^{ // Code to load Facebook Profile picture // ... SKSpriteNode *fbFrame = [SKSpriteNode spriteNodeWithTexture:facebookPicTexture]; dispatch_async(dispatch_get_main_queue(), ^{ NSString *strAlive = (NSString *)objc_getAssociatedObject(self, @"yourTag"); if (strAlive) { // self is my customised SKSpriteNode - so here I just add a sprite node of // a facebook picture to myself as a child [self addChild:fbFrame]; } }); }); dispatch_release(queue); 

Cuando no desee que el envío continúe el procedimiento cuando el auto (ViewController) ya no sea visible, escriba esto:

 objc_setAssociatedObject(self, @"yourTag", nil, OBJC_ASSOCIATION_RETAIN); 

En: - viewWillDisapear Or - viewDidDisapear Y renueva llamando al despacho cuando vuelvas a que a la pantalla.