2012-08-15 4 views
9

Ho visto molti post su overflow dello stack che affermano che il metodo viewDidLoad dei controller viene chiamato solo la prima volta che si accede al controller e non necessariamente ogni volta ma sempre a almeno una volta.viewDidLoad viene chiamato ogni volta che è presente una transizione in seguito

Questo non è quello che sto vedendo affatto! Ho messo insieme un semplice test per evidenziare questo: https://github.com/imuz/ViewDidLoadTest

Sembra per segues controller di navigazione e vista modale viewDidLoad viene sempre chiamato. L'unica volta in cui non viene chiamato è quando si passa da una scheda all'altra.

Ogni spiegazione viewDidLoad posso trovare contraddice questo:

e mele propria documentazione indicano che la vista viene scaricata solo quando la memoria è bassa .

Attualmente sto facendo l'inizializzazione in viewDidLoad facendo presupporre che sia chiamato con ogni transizione di seguito.

Mi manca qualcosa qui?

risposta

11

Credo che la documentazione di Apple stia descrivendo una situazione in cui il controller di visualizzazione non è stato deallocato. Se si utilizza un seguito, si sta creando l'istanza di un nuovo controller di destinazione e, essendo un nuovo oggetto, è necessario caricare una vista.

Nelle app basate su xib, a volte ho memorizzato nella cache un oggetto controller che sapevo che potrei riutilizzare frequentemente. In quei casi, si sono comportati in armonia con la documentazione in termini di quando una vista doveva essere caricata.

Modifica: Leggendo i collegamenti che hai incluso, non vedo alcuna contraddizione in essi. Anche loro stanno parlando di cose che accadono durante la vita di un oggetto del controller di visualizzazione.

+0

sua i bit dove si parla di vista di essere scaricati su che implica 'condizioni di scarsa memoria' sono lasciati in giro da default che non ho visto essere il caso. Quindi in realtà dipende dall'implementazione del controller genitore. Se si utilizzano i controller di navigazione, ogni volta viene creata e caricata una nuova istanza. Non così con i tabcontroller. – Imran

+1

Un modo per testare il comportamento della memoria bassa (sul simulatore) è di mettere su un controller di vista, coprirlo con un controller di vista modale, e utilizzare il Hardware-> opzione di avviso di memoria Simula. La vista del controllore nascosto dovrebbe scaricare e quindi ricaricare quando il modale viene rimosso. –

+0

oic interessante, mal provatelo. Immagino che qualsiasi vista che al momento non sia attiva è quindi candidata per lo scarico. – Imran

0

Viene chiamato ogni volta che la vista del controller viene caricata da zero (cioè richiesta ma non ancora disponibile). Se deallocate il controller e la vista va avanti con esso, allora verrà richiamato la volta successiva che istanziate il controller (ad esempio quando create il controller per spingerlo in modo modale o tramite i passaggi successivi). I controller di visualizzazione nelle schede non sono deallocati perché il controller di tabulazione li mantiene in giro.

12

La risposta di Phillip Mills è corretta. Questo è solo un miglioramento di esso.

Il sistema funziona come documentato.

Stai visualizzando viewDidLoad perché il controller di visualizzazione inserito nel controller di navigazione è una nuova istanza . È deve chiamare viewDidLoad.

Se si analizza un po 'più avanti, si vedrà che ciascuno di questi controller di vista viene deallocato quando vengono spuntati (basta inserire un breakpoint o un NSLog in dealloc). Questa deallocazione non ha nulla a che fare con il contenitore del controller di visualizzazione ... non controlla la vita del controller che usa ... sta solo mantenendo un forte riferimento ad esso.

Quando il controller viene estratto dallo stack del controller di navigazione, il controller nav rilascia il suo riferimento e poiché non vi sono altri riferimenti ad esso, il controller della vista verrà dealloc.

Il controller di navigazione contiene solo forti riferimenti per visualizzare i controller che si trovano nella pila attiva.

Se si desidera riutilizzare lo stesso controller, è il riutilizzo. Quando si utilizza lo storyboard, si rinuncia a tale controllo (in larga misura).

Supponiamo di avere un push seguito per visualizzare il controller Foo come risultato del tocco di un pulsante. Toccando questo pulsante, "il sistema" creerà un'istanza di Foo (il controller della vista di destinazione), quindi eseguirà il seguito. Il contenitore del controller ora contiene l'unico riferimento forte a quel controller di visualizzazione. Una volta terminato, il VC sarà dealloc.

Poiché ogni volta che crea un nuovo controller, viewDidLoad verrà chiamato ogni volta che viene presentato il controller.

Ora, se si desidera modificare questo comportamento e memorizzare nella cache il controller di visualizzazione per un successivo riutilizzo, è necessario farlo in modo specifico. Se non si utilizza segues storyboard, è facile dal momento che si sta effettivamente spingendo/popping il VC al controller nav.

Se, tuttavia, si utilizza lo storyboard, è un po 'più difficile.

Ci sono diversi modi per farlo, ma tutti richiedono una qualche forma di hacking. Lo storyboard stesso è incaricato di creare istanze di nuovi controller di visualizzazione. Un modo è quello di ignorare instantiateViewControllerWithIdentifier. Questo è il metodo che viene chiamato quando un seguito deve creare un controller di visualizzazione. Si chiama anche per i controller a cui non si attribuisce un identificatore (il sistema fornisce un identificativo univoco inventato se non ne assegni uno).

Nota, spero che questo sia principalmente per scopi didattici. Certamente non sto suggerendo questo come il modo migliore per risolvere i tuoi problemi, qualunque essi siano.

Qualcosa di simile ...

@interface MyStoryboard : UIStoryboard 
@property BOOL shouldUseCache; 
- (void)evict:(NSString*)identifier; 
- (void)purge; 
@end 
@implementation MyStoryboard 
- (NSMutableDictionary*)cache { 
    static char const kCacheKey[1]; 
    NSMutableDictionary *cache = objc_getAssociatedObject(self, kCacheKey); 
    if (nil == cache) { 
     cache = [NSMutableDictionary dictionary]; 
     objc_setAssociatedObject(self, kCacheKey, cache, OBJC_ASSOCIATION_RETAIN); 
    } 
    return cache; 
} 
- (void)evict:(NSString *)identifier { 
    [[self cache] removeObjectForKey:identifier]; 
} 
- (void)purge { 
    [[self cache] removeAllObjects]; 
} 
- (id)instantiateViewControllerWithIdentifier:(NSString *)identifier { 
    if (!self.shouldUseCache) { 
     return [super instantiateViewControllerWithIdentifier:identifier]; 
    } 
    NSMutableDictionary *cache = [self cache]; 
    id result = [cache objectForKey:identifier]; 
    if (result) return result; 
    result = [super instantiateViewControllerWithIdentifier:identifier]; 
    [cache setObject:result forKey:identifier]; 
    return result; 
} 
@end 

Ora, è necessario utilizzare questo storyboard. Sfortunatamente, mentre UIApplicazione mantiene lo storyboard principale, non espone un'API per ottenerlo. Tuttavia, ciascun controller di visualizzazione ha un metodo, storyboard per ottenere lo storyboard da cui è stato creato.

Se si stanno caricando i propri storyboard, è sufficiente istanziare MyStoryboard. Se si utilizza lo storyboard predefinito, è necessario forzare il sistema a utilizzare quello speciale. Ancora una volta, ci sono molti modi per farlo. Un modo semplice consiste nell'override del metodo accessor dello storyboard nel controller della vista.

È possibile rendere MyStoryboard una classe proxy che inoltra tutto a UIStoryboard, oppure è possibile isa-swizzle lo storyboard principale, oppure è sufficiente che il controller locale ne restituisca uno dal metodo storyboard.

Ora, ricorda, c'è un problema qui. Cosa succede se si spinge lo stesso controller di visualizzazione sullo stack più di una volta? Con una cache, lo stesso identico oggetto del controller di visualizzazione verrà utilizzato più volte. E 'davvero quello che vuoi?

Se no, allora è ora necessario gestire l'interazione con i contenitori del controller stessi in modo che possano controllare per vedere se questo controller è già noto da loro, nel qual caso una nuova istanza è necessario.

Quindi, non v'è un modo per ottenere i controllori nella cache durante l'utilizzo segues predefinita storyboard (in realtà ci sono alcuni modi) ... ma che non è necessariamente una buona cosa, e certamente non quello che si ottiene per default .

+0

Grazie per la risposta dettagliata. Sto bene senza mettere in cache i controller. Volevo solo capire il ciclo di scarico del carico un po 'di più in modo da poter capire esattamente cosa posso e non posso mettere in viewDidLoad. Adesso è molto più chiaro. – Imran

+1

In realtà, viewWillUnload e viewDidUnload sono stati deprecati, quindi non dovrebbe davvero mettere qualsiasi codice in là andando avanti. Gestire didReceiveMemoryWarning per liberare risorse quando si è sotto pressione di memoria. –

+0

Ahh proprio in IOS6. Sembra che non ci sia più alcun reale punto di vista su DidLoad, potrebbe anche essere un costruttore. Suppongo che questo significhi che gli avvertimenti sulla memoria non causeranno più il richiamo di viewDidUnload? Questo tipo di cose cambia perché significa che viewDidLoad viene chiamato sempre una volta sola? Al momento non ho IOS6 per testare. – Imran