¿Por qué el objeto no está desasignado cuando se usa ARC + NSZombieEnabled?

Convertí mi aplicación a ARC y me di cuenta de que un objeto alojado en uno de mis controladores de vista no estaba siendo desasignado cuando ese controlador de vista fue desasignado. Me llevó un tiempo descubrir por qué. He habilitado los objetos de Zombie para mi proyecto durante la depuración y esta resultó ser la causa. Considere la siguiente lógica de aplicación:

1) Los usuarios invocan la acción en RootViewController que hace que se RootViewController un SecondaryViewController y se presente a través de presentModalViewController:animated .

2) SecondaryViewController contiene un ActionsController que es una subclase NSObject .

3) ActionsController observa una notificación a través de NSNotificationCenter cuando se inicializa y deja de observar cuando se desasigna.

4) El usuario descarta SecondaryViewController para regresar a RootViewController .

Con Enable Zombie Objects desactivado, lo anterior funciona bien, todos los objetos son desasignados. Con Habilitar objetos Zombie en ActionsController no se desasigna aunque SecondaryViewController se desasigna.

Esto causó problemas en mi aplicación b / c NSNotificationCenter continúa enviando notificaciones a ActionsController y los controladores resultantes hacen que la aplicación se cuelgue.

Creé una aplicación simple que ilustra esto en https://github.com/xjones/XJARCTestApp . Mire el registro de la consola con Activar / desactivar objetos Zombie para verificar esto.

PREGUNTA (S)

  1. ¿Es este comportamiento correcto de Habilitar objetos Zombie?
  2. ¿Cómo debería implementar este tipo de lógica para eliminar el problema? Me gustaría seguir usando Habilitar objetos Zombie.

EDIT # 1: según la sugerencia de Kevin, he enviado esto a Apple y openradar a http://openradar.appspot.com/10537635 .

EDIT # 2: aclaración sobre una buena respuesta

Primero, soy un desarrollador con experiencia en iOS y entiendo totalmente ARC, objetos zombies, etc. Si me falta algo, por supuesto, agradezco cualquier iluminación.

En segundo lugar, es cierto que una solución alternativa para este locking específico es eliminar actionsController como observador cuando secondaryViewController está desasignado. También he encontrado que si establezco explícitamente actionsController = nil cuando secondaryViewController es desasignado, será desasignado. Ambas opciones no son una buena solución b / c. De hecho, requieren que utilice ARC pero el código como si no estuviera usando ARC (por ejemplo, nil iVars explícitamente en dealloc). Una solución específica tampoco ayuda a identificar cuándo sería un problema en otros controladores, por lo que los desarrolladores saben de forma determinista cuándo / cómo solucionar este problema.

Una buena respuesta explicaría cómo saber de manera determinista que necesita hacer algo especial con un objeto al usar ARC + NSZombieEnabled para que resuelva este ejemplo específico y también se aplique en general a un proyecto en conjunto sin dejar el potencial para otros similares. problemas.

Es muy posible que no exista una buena respuesta ya que esto puede ser un error en XCode.

¡gracias a todos!

Resulta que he escrito algunas tonterías serias

Si los zombies funcionaran como lo escribí originalmente, activar zombies podría generar innumerables falsos positivos …

Hay algo de isa-swizzling en marcha, probablemente en _objc_rootRelease , por lo que cualquier anulación de dealloc todavía debería ser llamada con zombies habilitados. Lo único que no sucederá con zombies es la llamada real a object_dispose , al menos no de forma predeterminada.

Lo gracioso es que, si hace un poco de registro, realmente verá que incluso con ARC habilitado, su implementación de dealloc llamará a su implementación de superclase.

En realidad estaba asumiendo que no vería esto en absoluto: dado que ARC genera estos funky métodos .cxx_destruct para deshacerse de cualquier __strong iva de una clase, esperaba que este método llamara a dealloc , si está implementado.

Aparentemente, establecer NSZombieEnabled en YES hace que no se .cxx_destruct a .cxx_destruct , al menos eso es lo que sucedió cuando .cxx_destruct tu proyecto de muestra:
los zombis apagados conducen a la traza inversa y los dos a los trapecistas, mientras que los zombies no producen retrocesos y solo un trato.

Si está interesado, el registro adicional está contenido en una bifurcación del proyecto de muestra ; funciona simplemente ejecutando: hay dos esquemas compartidos para zombies on / off.


Respuesta original (sin sentido):

Esto no es un error, sino una característica.

Y no tiene nada que ver con ARC.

NSZombieEnabled básicamente swizzles dealloc para una implementación que, a su vez, isa-swizzles tipo de objeto a _NSZombie – una clase ficticia que explota, tan pronto como le envíe un mensaje. Este es el comportamiento esperado y, si no estoy del todo equivocado, documentado.

Este es un error que ha sido reconocido por Apple en Technical Q & A QA1758 .

Puede solucionarlo en iOS 5 y OS X 10.7 comstackndo este código en su aplicación:

 #import  @implementation NSObject (ARCZombie) + (void) load { const char *NSZombieEnabled = getenv("NSZombieEnabled"); if (NSZombieEnabled && tolower(NSZombieEnabled[0]) == 'y') { Method dealloc = class_getInstanceMethod(self, @selector(dealloc)); Method arczombie_dealloc = class_getInstanceMethod(self, @selector(arczombie_dealloc)); method_exchangeImplementations(dealloc, arczombie_dealloc); } } - (void) arczombie_dealloc { Class aliveClass = object_getClass(self); [self arczombie_dealloc]; Class zombieClass = object_getClass(self); object_setClass(self, aliveClass); objc_destructInstance(self); object_setClass(self, zombieClass); } @end 

Encontrará más información sobre esta solución alternativa en mi publicación de blog Debugging with ARC and Zombies enabled .

Resulta que es un error de iOS. Apple me contactó e indicó que lo arreglaron en iOS 6.

para responder a la segunda pregunta, necesitarás eliminar al observador de NSNotification, lo que evitará que llame a la vista.

Normalmente, harías esto en el trato, pero con ese problema de zombie quizás no se llame. ¿Tal vez podrías poner esa lógica en viewDidUnload?

Debido a que tiene NSZombieEnabled abierto, esto permite que el objeto no llame a dealloc y coloque el objeto en un lugar especial. puedes cerrar NSZombieEnabled y volver a intentarlo. Y verifique si su código tiene la condición de retención de círculo.