iphone - viewDidLoad se llama dos veces en rootViewController en el inicio
viewcontroller swift (10)
En mi caso, no me di cuenta de que en realidad he asignado el rootViewController dos veces:
application:didFinishLaunchingWithOptions:
y applicationDidBecomeActive:
¿Alguien sabe por qué se llama al viewDidLoad
este View Controller''s
raíz View Controller''s
en el lanzamiento? ¡Me está volviendo loco!
Aquí está el seguimiento de la pila desde la primera vez a través de viewDidLoad
:
#0 0x0000276a in -[RootViewController viewDidLoad] at RootViewController.m:71
#1 0x3097548f in -[UIViewController view]
#2 0x00002734 in -[RootViewController initWithCoder:] at RootViewController.m:39
#3 0x30ab5ce4 in -[UIClassSwapper initWithCoder:]
#4 0x30514636 in _decodeObjectBinary
#5 0x30514035 in _decodeObject
#6 0x30ab5a1d in -[UIRuntimeConnection initWithCoder:]
#7 0x30514636 in _decodeObjectBinary
#8 0x30515f27 in -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:]
#9 0x305163b0 in -[NSArray(NSArray) initWithCoder:]
#10 0x30514636 in _decodeObjectBinary
#11 0x30514035 in _decodeObject
#12 0x30ab4dde in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:]
#13 0x30ab6eb3 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:]
#14 0x308f85f1 in -[UIApplication _loadMainNibFile]
#15 0x30901a15 in -[UIApplication _runWithURL:sourceBundleID:]
#16 0x308fef33 in -[UIApplication handleEvent:withNewEvent:]
#17 0x308fad82 in -[UIApplication sendEvent:]
#18 0x309013e1 in _UIApplicationHandleEvent
#19 0x32046375 in PurpleEventCallback
#20 0x30245560 in CFRunLoopRunSpecific
#21 0x30244628 in CFRunLoopRunInMode
#22 0x308f930d in -[UIApplication _run]
#23 0x309021ee in UIApplicationMain
#24 0x000022e4 in main at main.m:14
y la segunda vez:
#0 0x0000276a in -[RootViewController viewDidLoad] at RootViewController.m:71
#1 0x30ab50cd in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:]
#2 0x30ab6eb3 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:]
#3 0x308f85f1 in -[UIApplication _loadMainNibFile]
#4 0x30901a15 in -[UIApplication _runWithURL:sourceBundleID:]
#5 0x308fef33 in -[UIApplication handleEvent:withNewEvent:]
#6 0x308fad82 in -[UIApplication sendEvent:]
#7 0x309013e1 in _UIApplicationHandleEvent
#8 0x32046375 in PurpleEventCallback
#9 0x30245560 in CFRunLoopRunSpecific
#10 0x30244628 in CFRunLoopRunInMode
#11 0x308f930d in -[UIApplication _run]
#12 0x309021ee in UIApplicationMain
#13 0x000022e4 in main at main.m:14
Esto me sucedió cuando fusioné un proyecto desde el guión gráfico a la forma antigua usando xibs para construir vistas. La razón principal para volver a cambiar fue el hecho de que no podía presentar correctamente una vista modal correctamente. La forma en que normalmente lo hago es hacer que un método de delegado de un UIButton construya una instancia de un determinado controlador de vista, establezca algunas de sus propiedades (la más importante es el delegado, por lo que puedo descartar nuevamente el controlador de vista modal) y luego presentar De una manera modal. En la nueva forma de storyboard, esto se hace supuestamente con un segue. Personalizar la transición solo es factible haciendo una clase personalizada que amplíe la clase UIStoryboardSegue. Encuentro esta manera demasiada molestia en comparación con la forma simple en que solía ser, así que me fusioné de nuevo.
¿Cómo me hizo esto tener un controlador de vista cargado dos veces? Al transferir el código del proyecto del guión gráfico al proyecto xib, hice un par de xibs (uno para cada ViewController) y copié el objeto viewcontroller del guión gráfico. Esto llevó a un xib con en él no un viw, sino un controlador de vista; lo que significa que había puesto un controlador de vista en un controlador de vista (ya que el propietario del archivo también es una instancia del controlador de vista). No creo que en tu caso tuvieras este problema, pero espero que quizás ayude a alguien algún día.
Para solucionar este problema, mueva la vista desde el controlador de vista fuera del controlador de vista y al nivel raíz de la sección de objetos. Tanto el controlador de vista como su elemento de navegación deben eliminarse. Genere y ejecute y debería ver solo una asignación para el controlador de vista. Este es el propietario del archivo.
Extraño. No he visto este caso en particular, pero en general, debe asumir que viewDidLoad se puede llamar varias veces. Se llamará cada vez que se cargue un archivo de plumilla que hace referencia al controlador.
Para una aplicación simple con una sola punta, eso no debería suceder. Pero en una aplicación más compleja que puede cargar y descargar controladores de vista, esto sucede todo el tiempo.
Hice algo de depuración y esto es lo que encontré sobre el orden de carga de ViewController
:
initWithNibName:bundle: self = <original instance>, retainedOutlet = 0x0
loadView >>> self = <original instance>, retainedOutlet = 0x0
initWithCoder: self = <coder instance>, retainedOutlet = 0x0
initWithCoder: self = <coder instance>, retainedOutlet = 0x0
setView: self = <original instance>, retainedOutlet = 0x0
setRetainedOutlet: self = <original instance>, retainedOutlet = 0x1613c40
viewDidLoad self = <coder instance>, retainedOutlet = 0x0
awakeFromNib self = <coder instance>, retainedOutlet = 0x0
loadView <<<
viewDidLoad self = <original instance>, retainedOutlet = 0x1613c40
viewWillAppear: self = <original instance>, retainedOutlet = 0x1613c40
dealloc self = <coder instance>, retainedOutlet = 0x0
viewDidAppear: self = <original instance>, retainedOutlet = 0x1613c40
Durante el método loadView, se llama a initWithCoder:
y se crea una nueva copia del viewController
. esto es lo que se pasa a algunos de los métodos (como viewDidLoad
). La copia se destruye más tarde en una llamada dealloc. la buena noticia es que en esta copia, los puntos de venta retenidos no están configurados, por lo que puede usar esto como una prueba para saber si debe inicializar variables, llamar a otros métodos y, lo más importante, si debe liberar y destruir objetos durante el dealloc.
viewController
clave: el viewController
real tendrá configuradas sus propiedades de IBOutlet
retenidas. Si se encuentra en un método invalidado al que se llama varias veces, simplemente marque una de sus propiedades de IBOutlet
retenidas para NULL
. Si son NULL
, entonces regresa inmediatamente.
¿Alguien tiene alguna pista de por qué esto está sucediendo de esta manera?
Efecto secundario de esto: no puede usar awakeFromNib
forma confiable.
Me encontré con el mismo problema ya que estaba rediseñando un ViewController
desde cero para deshacerme del archivo XIB y hacer que la clase fuera reutilizable. Tuve esta segunda instancia de ViewController
que recibiría un mensaje viewDidLoad
seguido de un mensaje dealloc.
Descubrí que esto era el resultado de que el método loadView
no se había redefinido en el ViewController
. El loadView
predeterminado llamado awakeFromNib
, con la propiedad nibName
establecida en el nombre de la clase. Aunque había eliminado el archivo XIB del proyecto, todavía estaba en el directorio de la aplicación en el simulador.
Entonces, aunque podría simplemente restablecer los contenidos y la configuración del simulador para deshacerse de la segunda viewDidLoad
, una mejor manera podría ser simplemente redefinir loadView
siguiente manera:
- (void)loadView {
self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
self.view.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
}
Tiene sentido si considera la documentación de la UIViewController''s
vista UIViewController''s
:
Si accede a esta propiedad y su valor es actualmente nulo, el controlador de vista llama automáticamente al método
loadView
y devuelve la vista resultante. El métodoloadView
predeterminado intenta cargar la vista desde el archivo nib asociado con el controlador de vista (si existe). Si su controlador de vista no tiene un archivo de plumilla asociado, debe anular el métodoloadView
y usarlo para crear la vista raíz y todas sus subvistas.
No puedes asumir que viewDidLoad será llamado solo una vez. Si está inicializando objetos y desea una garantía, realice la inicialización ya sea en el método init o si está cargando desde un archivo de plumilla desde el método awakeFromNib.
Solo para agregar a esto, si está utilizando una función del sistema, como TouchID, entonces la aplicaciónWillResignActive en su AppDelegate se invocará y, si se dice, restablecer los controladores a un Controlador de raíz seguro, se volverá a activar y se ejecutará el parámetro SegueWithIdentifier (self.MAIN_SEGUE , remitente: yo) no disparará!
Tuve este mismo problema cuando mi aplicación se lanzó por primera vez. Lo que encontré fue que en mi archivo MainWindow.xib, estaba configurando la salida viewController
mi Delegado de la viewController
salida viewController
mi Ventana a mi controlador de vista raíz. Cuando construyes un archivo de proyecto basado en la vista en Xcode, las didFinishLaunchingWithOptions
tu delegado de didFinishLaunchingWithOptions
se didFinishLaunchingWithOptions
previamente con:
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
Creo que el self.viewController
ivar está instanciado desde MainWindow.xib antes de que se didFinishLaunchingWithOptions
. Luego, el código rootViewController
previamente de arriba establece el rootViewController
la ventana del rootViewController
la ventana. Entonces, si, en conjunto, especifica la salida rootViewController
para la Ventana en su archivo MainWindow.xib, su controlador de vista raíz se creará dos veces y se agregará dos veces como el controlador de vista raíz de la Ventana.
Tuve este problema pero pude arreglarlo.
Solución :
Cambie el nombre de la clase de controlador de vista que se carga dos veces.
Detalles :
Renombrarlo y hacer que el nuevo nombre sea algo completamente nuevo. Cambiar el nombre del archivo no detiene el problema de carga doble. Crear un nuevo proyecto (como lo sugieren otros) puede ser excesivo, ¡al menos intente primero con las soluciones más simples! Cambiar el nombre de la clase del destino VC.
Sugerencia : si cambiar el nombre de la clase soluciona su problema, obviamente tendrá que actualizar todas sus referencias a esa clase. Puede acelerar esto utilizando Command + Shift + F para la búsqueda en todo el proyecto.
Tuve un problema similar y fue el resultado de cambiar el nombre de mi archivo XIB y su clase ViewController
(Propietario del archivo). No haga eso, ya que realmente obtuvo las vistas y delegados mal definidos dentro del XML y no fue recuperable. Mientras tanto, tenía una referencia a la carga del VC original que se suponía era mi nuevo VC. Creo que eso causó que el padre se recreara y luego el VC que realmente intenté invocar. Básicamente, creé una recursión indirecta al VC que tiene entradas viewDidLoad
x2 en mi rastreo.
No creo que exista una razón válida para x2 viewDidLoad
ya que es una génesis y puede invocar otra inicialización con las condiciones previas supuestas incorrectas. Cada vez que he visto x2 viewDidLoad, fue un error de codificación de mi parte, bastante a menudo cuando estaba refactorizando y moviendo las clases de VC.
Si hay una razón válida para algo más que en la llamada viewDidLoad
, por favor, alguien (usted está escuchando a Apple Dev) explíquela con detalles técnicos: he estado buscando esa respuesta durante meses.