Ver controller enviado un post aunque se ha desasignado

No estoy seguro si algo ha cambiado en el iPhone SDK 3.0, pero estoy recibiendo el error más extraño. Tengo una jerarquía del controller de vista donde cambio entre controlleres de visualización dependiendo de la orientación de la interfaz. Por lo que puedo decir, el error se produce cada vez que giro la interfaz, un controller de vista que ha sido desasignado está siendo enviado a un post de shouldAutorotateToInterfaceOrientation. Esta es la function inversa para el error:

#0 0x01dc43a7 in ___forwarding___ #1 0x01da06c2 in __forwarding_prep_0___ #2 0x002e6733 in -[UIWindow _shouldAutorotateToInterfaceOrientation:] #3 0x002e6562 in -[UIWindow _updateToInterfaceOrientation:duration:force:] #4 0x002e6515 in -[UIWindow _updateInterfaceOrientationFromDeviceOrientation] #5 0x0004d63a in _nsnote_callback #6 0x01d8f005 in _CFXNotificationPostNotification #7 0x0004aef0 in -[NSNotificationCenter postNotificationName:object:userInfo:] #8 0x0045b454 in -[UIDevice setOrientation:] #9 0x002d6890 in -[UIApplication handleEvent:withNewEvent:] #10 0x002d16d3 in -[UIApplication sendEvent:] #11 0x002d80b5 in _UIApplicationHandleEvent #12 0x024c2ef1 in PurpleEventCallback #13 0x01d9bb80 in CFRunLoopRunSpecific #14 0x01d9ac48 in CFRunLoopRunInMode #15 0x024c17ad in GSEventRunModal #16 0x024c1872 in GSEventRun #17 0x002d9003 in UIApplicationMain #18 0x00002d50 in main at main.m:14 

El error que se está imprimiendo a la console de debugging con NSZombieEnabled es:

 2009-10-18 20:28:34.404 Restaurants[12428:207] *** -[ToolbarController respondsToSelector:]: message sent to deallocated instance 0x3b2b2a0 (gdb) continue Current language: auto; currently objective-c 2009-10-18 20:31:43.496 Restaurants[12428:207] *** NSInvocation: warning: object 0x3b2b2a0 of class '_NSZombie_BeltToolbarController' does not implement methodSignatureForSelector: -- trouble ahead 2009-10-18 20:31:43.496 Restaurants[12428:207] *** NSInvocation: warning: object 0x3b2b2a0 of class '_NSZombie_BeltToolbarController' does not implement doesNotRecognizeSelector: -- abort 

Lo que no puedo entender es por qué el sistema está tratando de enviar posts a este controller aunque haya sido desasignado y hay una manera de decirle al sistema que el controller ya no existe.

[ACTUALIZACIÓN]: he creado un proyecto de ejemplo que reproduce el error: download

Cargue la aplicación y luego cambie la orientación del simulador varias veces de Horizontal a Vertical y debería ocurrir. He probado la misma pieza de código en un teléfono físico y se comporta exactamente de la misma manera, así que este no es un problema relacionado con el simulador.

[ACTUALIZACIÓN]: He usado una de mis requestes de soporte con el equipo técnico de Apple para ver si pueden ayudarme a llegar al final de esto. Publicaremos la solución, si tienen una, aquí. Gracias por la ayuda hasta ahora.

Evidentemente, el controller todavía está registrado para la notificación en cuestión. enviar -removeObserver: auto al centro de notifications en su método -dealloc.

Entonces, después de una semana de espera, el Soporte técnico para desarrolladores de Apple logró ayudarme a resolver mi problema. Aquí está su respuesta:

"Revisé su código y encontré algunas cosas sobre las que debe preocuparse, algunas de las cuales pueden contribuir a su problema. En su fuente ControllerSwitchAppDelegate.m , está implementando el método" didRotate ". Puede que valga la pena comprobarlo las notifications de orientación del dispositivo en el nivel del controller de visualización en lugar del nivel de aplicación UIA. Esto hará que su código sea mucho más simple y encapsulado, permitiendo que cada controller de vista que se muestre maneje su propia lógica de rotation. También está utilizando múltiples controlleres de vista al mismo time , ese ser, las properties de "vista" se agregan y se eliminan cuando se gira el dispositivo. Este no es exactamente el enfoque común para usar el UIKit. La idea es presentar un controller de vista (o su propiedad de vista) en un time y no tener un controller de vista padre intercambiar en diferentes controlleres de subview. Concedido en la superficie, su enfoque parece factible, pero a la larga, recomiendo un enfoque diferente.

Tenemos una muestra llamada "AlternateViews", que se puede encontrar en: http://developer.apple.com/iphone/library/samplecode/AlternateViews/index.html

En esta muestra, hace prácticamente todo lo que necesita. Proporciona un controller de vista "alternativo" para una orientación de dispositivo determinada. Simplemente presenta un controller de vista sobre otro utilizando "presentModalViewController" con una propiedad de transición llamada "modalTransitionStyle" que le dará un efecto de transición gradual ".

Lo que terminé haciendo fue usar un controller de super visión que presentaba y desestimaba los controlleres de vista. En lugar de intercambiar controlleres de vista y eliminar sub vistas usando AppDelegate.

Mucha confusión aquí … veamos si esto ayuda:

Lo que no puedo entender es por qué el sistema está tratando de enviar posts a este controller aunque haya sido desasignado y hay una manera de decirle al sistema que el controller ya no existe.

Mientras la desasignación destruye la instancia de un object, no destruye las references a las instancias. Con la detección de Zombie activada, la desasignación hace que el time de ejecución sustituya a un zombie por la instancia. El zombie luego detecta y registra cuando se envía un post.

La razón por la que esto sucede es porque el object ha sido desasignado sin eliminar también todas las references al object del gráfico de objects de la aplicación. En este caso, parece que el controller desasignado nunca se desarmó como el controller de la instancia de UIWindow.

Es decir, el procesamiento de la notificación es un arenque rojo . En ese backtrace, la notificación ya se ha entregado y UIWindow está en medio de procesarlo. Por lo tanto, el problema está en algún lugar entre la UIWindow y su aplicación. Lo más probable es que su object haya sido desasignado antes de ser eliminado de la window como su controller o delegado.

Antes de que su progtwig se haga realmente con un object, antes de enviar la última llamada de -release que equilibra el último existente, -retain su aplicación causada o -retain , debe asegurarse de que se destruyan todas las references débiles al object. Por ejemplo, si su object es el delegado de una UIWindow, asegúrese de establecer el delegado de la window en nil (u otro object) antes de enviar esa última -release .

Ahora, en este caso, también puede ser simplemente que ha sobrepasado el object. Es posible que todavía necesites el object, pero una -autorelease extra o una -autorelease algún lugar está causando que se destruya antes de que hayas terminado con él.

Si a alguien todavía le importa, una solución simple es crear una vista de controller raíz + vista que nunca cambie.

Dado SomeViewController + SomeView A y SomeViewController + SomeView B, si agrega la vista A a la window como una subvista, luego agrega la vista B como una subvista y elimina la vista A, la aplicación se bloqueará al girar.

Para evitar el locking, cree un UIViewController + UIView X genérico y agregue la vista X a la window como una subvista. Agregue y elimine las vistas A y B hacia / desde la vista X, no la window directamente. La aplicación ya no se bloqueará.

Tenga en count que no es suficiente simplemente agregar una vista a la window; debe agregar una vista que tenga un controller de vista.

Esta es una buena razón para configurar ToolbarController = nil después de soltarlo. Es seguro enviar posts a cero, pero no a direcciones de objects desasignados. En este caso, enviar un post a una dirección de un object que no sale, lo que está causando un locking.

¡Es una pérdida de time comprobar el ToolbarController != nil antes de enviar el post, porque si es nulo, puede enviar el post de manera segura. si no es nulo y válido, devolverá SÍ o NO. Pero si se trata de un puntero a la memory desasignada (como parece que parece tener aquí), de todas maneras se va a bloquear.

También me he topado con este problema. El order de los events es:

(1) cree el object UIWindow único de la aplicación

(2) agregue una subvista administrada por un controller de vista a la window

(3) eliminar la primera vista y agregar una nueva

Si giro el dispositivo después, la aplicación se bloquea debido a un post enviado al controller de vista desasignado. (Bueno, en realidad lo está enviando a un subdirector del primer controller de vista). Está intentando enviar: [respondsToSelector: @selector (shouldAutorotateToInterfaceOrientation :)].

Si su aplicación solo se ejecuta en modo vertical, puede hacer que el problema desaparezca agregando una categoría a UIWindow que anula _houldAutorotateToInterfaceOrientation: para devolver NO para todo lo que no sea el modo vertical.

Obviamente, esta no es una solución real. He revisado mi código con doble triple y no encuentro ninguna razón por la que la window deba enviar este post al controller para una vista que se ha eliminado de la pantalla y se ha desasignado. Este problema también parece haber aparecido en 3.0 y no antes. Tal vez estoy haciendo algo estúpido, pero realmente parece haber algo extraño en el trabajo aquí.

También tuve el mismo problema, pero no pude dejar a un controller merodeando como sugerencia de Mark Smith. Eliminar el controller de vista con autorelease en lugar de liberar o una propiedad retenida parecía hacer el truco.

Parece que el UIWindow / framework padre necesita que el controller de vista cuelgue un poco más de time para permitirle eliminar el enlace de delegado.

Había estado experimentando el mismo problema hasta que se eliminaron algunas líneas de código 'chocantes' que usé para impulsar la animation como las siguientes:

UIView* superv = navigationController.view.superview;

[navigationController.view removeFromSuperview];

[superv addSubview:navigationController.view];

Definitivamente, lo anterior es un path de "ruptura", ya que 3.0 SDK había sido emitido por Apple. Me he visto forzado a usar el enfoque push / pop en su lugar. No tuve el problema con 2.x también. Asegúrate de que no tienes algo similar en tu código.

Espero eso ayude.

He publicado una respuesta aquí:

https://stackoverflow.com/a/19237139/539149

Tuve un lugar que decía:

 [viewController release]; viewController = NULL; 

Lo que causó la liberación se llama dos veces (por lo que la memory se liberó de inmediato), pero el zombie no se reveló hasta que un object propiedad de iOS trató de hacer reference al object más adelante en el hilo principal.