Pergunta

Alguém sabe por que isso View Controller's viewDidLoad raiz está sendo chamado duas vezes no lançamento? Ele está me deixando louco!

aqui está o rastreamento de pilha da primeira vez através 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

e a 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
Foi útil?

Solução

Estranho. Eu não tenho visto neste caso particular, mas em geral, você deve assumir que viewDidLoad pode ser chamado várias vezes. Ele vai ter chamado sempre que um arquivo nib que as referências que o controlador é carregado.

Para um aplicativo simples com apenas um bico, que não deveria acontecer. Mas em um aplicativo mais complexo que pode carregar e descarregar controladores de vista, isso acontece o tempo todo.

Outras dicas

Eu tive esse mesmo problema quando meu aplicativo foi o primeiro lançamento. O que descobri foi que no meu arquivo MainWindow.xib, eu estava assentado tanto a minha saída viewController da App delegado, e tomada rootViewController da minha janela para o meu controlador de vista raiz. Quando você constrói um arquivo de projeto de exibição com base no Xcode, didFinishLaunchingWithOptions do seu App Delegado será pré-preenchida com:

self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;

Eu acredito que o ivar self.viewController é instanciado a partir MainWindow.xib antes didFinishLaunchingWithOptions é chamado. Em seguida, o código pré-preenchido acima conjuntos rootViewController da janela. Assim, se, em conjunto, você especifica a tomada rootViewController para a janela no seu arquivo MainWindow.xib, o controlador de vista raiz vai realmente ser criado duas vezes e adicionado como controlador de vista raiz da janela duas vezes.

Eu fiz alguma depuração e aqui está o que eu encontrei sobre a ordem ViewController carregamento:

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 o método loadView, initWithCoder: é chamado e uma nova cópia do viewController é criado. isso é o que é passado para alguns dos métodos (como viewDidLoad). a cópia é destruída depois de uma chamada dealloc. a boa notícia é que nesta cópia, manteve tomadas não estão configurados, de modo que você pode usar isso como um teste para saber se você deve inicializar variáveis, chamar outros métodos, e mais importante, se você deve liberar e destruir objetos durante dealloc.

takeaway Key: o viewController verdadeira terão suas propriedades IBOutlet retidos configurado. se você estiver em um método substituído que está recebendo chamado várias vezes, basta verificar uma das suas propriedades IBOutlet retidos NULL. se eles são NULL, em seguida, retornar imediatamente.

Alguém tem alguma pista de por que isso está acontecendo dessa maneira?

efeito colateral disso:. Você não pode usar awakeFromNib confiável

Você não pode assumir viewDidLoad será chamado apenas uma vez. Se você está inicializando objetos e quer uma garantia de fazer a inicialização, quer no método init ou se você é o carregamento de um arquivo nib do método awakeFromNib.

Eu tive um problema semelhante e foi resultado de renomear meu arquivo XIB e sua classe ViewController (dono do arquivo). Não faça isso - como ele realmente tem os pontos de vista e delegados mal definida dentro do XML e não era recuperável. Enquanto isso, eu tinha uma referência à carga do VC original que era suposto ser o meu novo VC. Eu acredito que causou o pai para se recriar e, em seguida, o VC eu estava realmente tentou invocar. Basicamente, eu criei uma recursão indireta ao VC que tem entradas viewDidLoad x2 no meu rastro.

Eu não acho que haja qualquer razão válida para viewDidLoad x2, pois é uma gênese e pode invocar outra inicialização com o erro assumido pré-condições. Toda vez que eu vi o x2 viewDidLoad, foi um erro de codificação da minha parte - muitas vezes quando eu estava refatoração e movendo as classes VC torno

.

Se houver uma razão válida para mais de plantão viewDidLoad, por favor, alguém (Apple Dev você está ouvindo) explicá-lo em detalhe técnico -. Fui pesquisar por essa resposta há meses

Eu tive esse problema, mas foi capaz de corrigi-lo.

Solução :

Mudar a classe controlador de vista de que está a carregar duas vezes.

Detalhes :

Renomear-lo e fazer o novo nome de algo inteiramente novo. Mudar o nome do arquivo não pára o problema duas vezes por carga. Criando um novo projeto (como sugerido por outros) pode ser um exagero, pelo menos, tentar as soluções mais simples em primeiro lugar! Renomeie a classe do destino VC.

Dica : Se renomear a classe resolve o seu problema, você então, obviamente, tem que atualizar todos os seus referências a essa classe. Você pode acelerar isso usando Command + Shift + F para o projeto em todo o encontrar.

Eu corri para o mesmo problema que eu estava redesenhar um ViewController a partir do zero para se livrar do arquivo XIB e fazer o reutilizável classe. Eu tinha essa segunda instância ViewController que iria receber uma mensagem viewDidLoad seguido por uma mensagem dealloc.

Eu descobri este foi o resultado do método loadView não ser redefinido no ViewController. O loadView padrão chamado awakeFromNib, com o conjunto de propriedades nibName para o nome da classe. Mesmo que eu tinha removido o arquivo XIB do projeto, ele ainda estava no diretório do aplicativo no simulador.

Assim, mesmo que você poderia apenas redefinir os conteúdos e definições do simulador para se livrar da segunda viewDidLoad, uma maneira melhor pode ser para loadView redefine como este:

- (void)loadView {
    self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
    self.view.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; 
}

Não faz sentido se você considerar a documentação para a propriedade vista UIViewController's:

Se você acessar esta propriedade e seu valor é atualmente zero, a visão controlador chama automaticamente o loadView método e os retornos resultante vista. O loadView padrão método tenta carregar a vista o arquivo nib associada à vista controlador (se qualquer). Se a sua visão O controlador não tem um associado arquivo nib, você deve substituir o método loadView e usá-lo para criar a exibição de raiz e todos os seus subviews.

No meu caso, eu não percebi que eu realmente atribuído o RootViewController duas vezes em:

application:didFinishLaunchingWithOptions: e applicationDidBecomeActive:

Só para acrescentar a isto, se você estiver usando uma função do sistema, tais como TouchID, em seguida, applicationWillResignActive no seu AppDelegate vai ter invocado e se você estiver digamos, reajuste controladores para um controlador Root seguro, em seguida, você se invocaram, e performSegueWithIdentifier (self.MAIN_SEGUE, remetente: self)! não irá fogo

Isso aconteceu comigo quando eu fundiu um projeto a partir do storyboard à maneira antiga usando XIBs para a construção de pontos de vista. A principal razão para a mudança de volta foi o fato de eu não poderia colocar corretamente uma vista modal corretamente. A forma I geralmente é por ter um método delegado a partir de um UIButton construir um exemplo de um certo viewcontroller, definir algumas das suas propriedades (a mais uma importação sendo o delegado que eu possa descartar adequadamente o controlador de vista modal de novo) e em seguida presente -lo de forma modal. No novo modo storyboard, este é supostamente feito com um segue. Personalizando a transição só é factível, fazendo uma classe personalizada que estende a classe UIStoryboardSegue. Acho que desta forma ter muito trabalho em comparação com a maneira simples que costumava ser, então eu mescladas de volta.

Como é que isso causa-me a ter uma carga viewcontroller duas vezes? Ao transferir o código do projeto de storyboard para o projeto xib, eu fiz um par de XIBs (um para cada ViewController) e Copiado o objeto viewcontroller do storyboard. Isso levou a um xib com nela não um viw, mas um viewcontroller; o que significa que eu tinha colocado um viewcontroller em um viewcontroller (desde o dono do arquivo também é uma instância da viewcontroller). Eu não acho que no seu caso que você teve este problema, mas eu espero que talvez ajude alguém algum dia.

Para corrigir esse movimento a vista do controlador de vista do controlador de vista e para o nível raiz da seção objetos. Tanto o controlador de vista e é item de navegação deve ser suprimida. Construir e executar e você deve ver apenas uma alocação para o controlador de vista. Este é o dono do arquivo.

E se o seu código fez o acesso a propriedade vista quando não é ainda carregado, o controlador de vista criará vista apenas vazio e isso poderia desencadear view did load acidentalmente.

A maioria erro comum está acessando propriedade vista durante a inicialização. Pode haver alguma acessor da propriedade (setter) que é invocado por xib deve acessar a propriedade vista acidentalmente.

E se alguma propriedade é anotada com IBInspectable você deve ter para verificar isViewLoaded antes de aplicar algum valor à vista.


-(void) setSomeProperty:(UIColor*) someColor
{
  _someColor = someColor;
  if(self.isViewLoaded) {
    // self.view causes view creation and invokes 'viewDidLoad' then the view is not ready yet.
    self.view.backgroundColor = someColor;
  }
}

-(void) viewDidLoad
{
  [super viewDidLoad]
  if(_someColor){
    self.view.backgroundColor = _someColor;
  }
}

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top