2009-04-26 4 views
31

Ho una vista contenente un UIWebView che sta caricando una mappa di google (quindi un sacco di javascript, ecc.). Il problema che ho è che se l'utente preme il pulsante "indietro" sulla barra di navigazione prima che la visualizzazione web abbia completato il caricamento, non è chiaro a me come comunicare in modo ordinato alla vista Web per interrompere il caricamento e rilasciarlo, senza ottenere messaggi inviati all'istanza deallocata. Inoltre, non sono sicuro che una visualizzazione Web piaccia che la sua vista contenitore scompaia prima che sia terminata (ma non ho scelta se l'utente preme il pulsante Indietro prima che venga caricato).Come arrestare in modo sicuro un UIWebView di caricamento in viewWillDisappear?

Nel mio gestore viewWillDisappear ho questo

map.delegate=nil; 
[self.map stopLoading]; 

questo sembra gestire la maggior parte dei casi OK, come nil'ing il delegato lo interrompe l'invio del didFailLoadWithError al mio controller della vista. Tuttavia se rilascio la visualizzazione Web nel metodo dealloc della mia vista, a volte (a intermittenza) riceverò comunque un messaggio inviato all'istanza deallocata, che sembra essere correlata al javascript in esecuzione nella pagina effettiva, ad esempio:

-[UIWebView webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:]: message sent to deallocated instance 0x4469ee0 

Se semplicemente non rilascio la webview, allora non ottengo questi messaggi anche se penso che sto perdendo la vista sul web.

Se io non mando il messaggio 'StopLoading', e rilasciare semplicemente il WebView entro viewWillDisappear, poi vedo messaggi come questo:

/SourceCache/WebCore/WebCore-351.9.42/wak/WKWindow.c:250 WKWindowIsSuspendedWindow: NULL window. 

eventualmente collegate, a volte (di nuovo totalmente intermittente) ottiene un brutto heisenbug in cui facendo clic sul pulsante Indietro sulla barra di navigazione di un'altra vista verrà visualizzato il titolo, ma non la vista. In altre parole, rimango con il titolo della vista n sullo stack, ma la vista mostrata è ancora la vista n + 1 (il risultato è che sei intrappolato su questo schermo e non puoi tornare alla vista radice - puoi andare altra direzione, ovvero spingere più visualizzazioni e tornare alla vista che non è stata visualizzata correttamente, ma non alla vista radice. L'unica via d'uscita è uscire dall'app). Altre volte la stessa sequenza di spinte e pop sulle stesse viste funziona bene.

Questo particolare mi sta facendo impazzire. Penso che possa essere correlato alla scomparsa della vista prima che venga caricata la vista web, ovvero in questo caso sospetto che possa scrivere sulla memoria e confondere lo stack di visualizzazione. Oppure, questo potrebbe essere completamente non correlato e un bug da qualche altra parte (non sono mai stato in grado di riprodurlo in modalità di compilazione di debug, accade solo con le impostazioni di build di rilascio quando non riesco a guardarlo con gdb :-). Dalle mie sessioni di debug, non penso di esagerare nel rilasciare nulla. E mi sembra di essere in grado di attivarlo solo se a un certo punto ho colpito la vista che ha la vista web, e non succede subito dopo.

+3

Se fossi in te, registrerei un bug con Apple. La vista Web non dovrebbe ricevere alcun messaggio dopo che è stato deallocato: deve terminare l'elaborazione JS e il caricamento dell'URL quando è deallocato. Ho visto il problema degli oggetti di navigazione occasionalmente, ma solo dopo aver installato iPhone OS 3.0 beta. Forse anche questo è un bug del sistema operativo? –

+0

sì è quello che pensavo (dealloc dovrebbe ripulire). I documenti Apple non sono generalmente molto chiari sul contratto con gli oggetti che forniscono. Non ho mai visto la cosa della nav prima e succede abbastanza regolarmente nel mio programma che penso che debba essere qualcosa che sto facendo - si spera legato a questa cosa della webview se riesco a risolverlo. – frankodwyer

+0

oh e per aggiungere, questo succede su 2.2.1 – frankodwyer

risposta

52

Una variante di questo dovrebbe risolvere sia i problemi fuoriuscito e zombie:

- (void)loadRequest:(NSURLRequest *)request 
{ 
    [self retain]; 
    if ([webView isLoading]) 
     [webView stopLoading]; 
    [webView loadRequest:request]; 
    [self release]; 
} 
- (void)webViewDidStartLoad:(UIWebView *)webView 
{ 
    [self retain]; 
} 
- (void)webViewDidFinishLoad:(UIWebView *)webView 
{ 
    [self release]; 
} 
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 
{ 
    [self release]; 
} 

- (void)viewWillDisappear 
{ 
    if ([webView isLoading]) 
     [webView stopLoading]; 
} 

- (void)dealloc 
{ 
    [webView setDelegate:nil]; 
    [webView release]; 
    [super dealloc]; 
} 
+0

sì buona risposta, stavo pensando di provare qualcosa di simile. la tua risposta ha alcune cose a cui non ho pensato, quindi proverò e se funzionerà accetterò questa. Sfortunatamente la ricompensa scadrà prima di avere il tempo di provare questo - ma quello che farò è riaprire la taglia e accettare la risposta in quel caso (solo per ricordare a me stesso, è stata 250 taglie). – frankodwyer

+0

Non mi preoccuperei troppo della taglia. È solo un numero (inoltre, metà è assegnato alla risposta più alta in ogni caso;) – rpetrich

+0

alla fine è riuscito a provarlo - sembra abbastanza buono finora. Nessun messaggio divertente e sembra persino aver curato l'heisenbug - era piuttosto intermittente, anche se così non lo farò mai più fino a quando non ne ho provato altri. – frankodwyer

0

Un semplice messaggio release in dealloc dovrebbe essere sufficiente.

Il tuo secondo problema sembra una vista prematuramente deallata, ma non posso dire molto senza vedere un po 'di codice.

+0

sì un semplice il rilascio in dealloc è quello che ho avuto, e questo è ciò che risulta nel messaggio che viene inviato all'istanza deallocata. La webview è dichiarata con @property (nonatomic, retain) se questo fa la differenza. Inoltre, ho NSZombie che controlla e in caso contrario non vedo alcun messaggio che vada a viste deallocate - anche come ho detto non posso riprodurlo affatto in modalità Debug, accade solo in modalità di rilascio. – frankodwyer

1

Ci sono alcuni modi per gestirlo, ma questo dovrebbe funzionare. Vuoi il messaggio didFailLoadWithError, è quello che ti dice che è stato fermato.

Impostare un flag isLeaving = YES; Invia la Webview a stopLoading.

In didFailLoadWithError :, verificate per l'errore che si ottiene quando il WebView ferma:

if ((thiserror.Codice == NSURLErrorCancelled) & & (isLeaving == SI)) {

[otherClass performSelector: @selector (shootWebview) withObject: nil withDelay: 0]

}

rilasciare la WebView in shootWebview:


varianti: se si vuole essere cavalier su di esso, si può fare la performSelector: withObject: withDelay: con un ritardo di [fillintheblank], lo chiamano 10-30 seco nds senza l'assegno e quasi certamente te la caverai, anche se non lo consiglio.

Si può fare in modo che il didFailLoadWithError imposti un flag e lo pulisca da qualche altra parte.

o il mio preferito, forse non è necessario dealloc tutto quando si lascia. Non visualizzerai mai più quel contenitore della vista? perché non tenerlo in giro riutilizzarlo?

Poiché il debug è diverso dal problema di rilascio, è consigliabile verificare la configurazione per accertarsi che sia esattamente la stessa. La taglia era nella parte riproducibile della domanda, giusto? ;-).

- Oh, aspetta un secondo, è possibile che tu stia prendendo un intero contenitore View con la WebView. Puoi fare una variazione su quanto sopra e aspettare di rilasciare l'intero contenitore in shootWebView.

+0

il mio problema è che una volta ricevuto il messaggio viewWillDisappear, il controller della mia vista sta andando via e sarà presto deallocato (insieme a tutto ciò che contiene, cioè la webview). Non vedo alcun modo per impedire che il controller di visualizzazione venga deallocato (senza perdite) poiché non sono io a farlo. Quindi qualsiasi messaggio delegato verrà (a volte) finito per passare all'istanza dealloced del mio controller. – frankodwyer

+0

Questo suona fastidioso anche mentre lo scrivo, ma suppongo che tu possa [conservare autonomamente] prima della tua webview caricare le chiamate e bilanciare con [self release] nel didfinishload e didfailloads per assicurarti che tutto rimanga intorno fino a che WebViews non sia completato correttamente. – dieselmcfadden

+0

sì, stavo pensando da quelle parti, e come te ho pensato che fosse un po 'stordito - non capisco perché non dovrebbe funzionare così. Proverò questo, sulla falsariga della risposta di rpetrich. – frankodwyer

1

Il bug UINavigationController che stai descrivendo nella seconda parte del tuo post potrebbe essere correlato al trattamento di avvisi di memoria. Ho sperimentato questo fenomeno ed io "sono già stati in grado di riprodurlo in mostra n nello stack simulando un avviso di memoria durante la visualizzazione vista (n + 1) nello stack.

UIWebView è un mangiatore di memoria, in modo da ottenere gli avvertimenti di memoria non sarebbero sorprendenti se usati come parte di una gerarchia di viste

+0

È interessante, grazie. Leggerò su ciò che dovrei fare in risposta a un avvertimento di memoria (in questo momento non lo gestisco affatto). In realtà non ho visto questo problema da quando mi sono spostato su 3.0 e scaricato la webview a favore di mapkit, e in genere rafforzato su altre aree di gestione della memoria. – frankodwyer

0

Ho avuto un problema simile a questo utilizzando UIWebView in OS3 - questa descrizione è stata un buon punto di partenza, tuttavia ho trovato che semplicemente nil'ing out the web view delegate prima di rilasciare la webView risolto il mio problema

Leggere il codice di esempio (la risposta accettata - sopra) - sembra un sacco di overkill.Eg [webView release] e webView = nil lines do esattamente la stessa cosa dato il modo in cui l'autore descrive la variabile è dichiarata (quindi non hai bisogno di entrambi). Neanche io sono del tutto convinto da tutte le linee di ritenzione e di rilascio - ma immagino che il tuo chilometraggio varierà.

0

Possibly related, I sometimes (again totally intermittent) get an ugly heisenbug where clicking the back button on some other view's navbar will pop the title, but not the view. In other words I get left with the title of view n on the stack, but the view showing is still view n+1 (the result is you're trapped on this screen and cannot get back to the root view - you can go the other direction, i.e. push more views and pop back to the view that didn't pop corrrectly, just not to the root view. The only way out is to quit the app). At other times the same sequence of pushes and pops on the same views works fine.

Ho lo stesso problema, quando sono controller di navigazione l'uso con vista controllori nello stack> 2 e la corrente indice di vista del regolatore> 2, se si verifica un memoryWarning in questo momens, solleva gli stessi problemi.

C'è una sola soluzione, che ho trovato dopo molti esperimenti con sovrascrivere i metodi pop e push in NavigationController, con i controller stack view, con visualizzazioni e supervisioni per ViewControllers impilati, ecc.

#import <UIKit/UIKit.h> 
#import <Foundation/Foundation.h> 

@interface FixedNavigationController : 
UINavigationController <UINavigationControllerDelegate>{ 

} 

@end 

#import "FixedNavigationController.h" 

static BOOL bugDetected = NO; 

@implementation FixedNavigationController 

- (void)viewDidLoad{ 
    [self setDelegate:self]; 
} 

- (void)didReceiveMemoryWarning{ 
    // FIX navigationController & memory warning bug 
    if([self.viewControllers count] > 2) 
     bugDetected = YES; 
} 

- (void)navigationController:(UINavigationController *)navigationController 
didShowViewController:(UIViewController *)viewController 
animated:(BOOL)animated 
{ 

    // FIX navigationController & memory warning bug 
    if(bugDetected){ 
     bugDetected = NO; 

     if(viewController == [self.viewControllers objectAtIndex:1]){ 
      [self popToRootViewControllerAnimated:NO]; 
      self.viewControllers = [self.viewControllers arrayByAddingObject:viewController]; 
     } 
    } 
} 

@end 

Funziona bene per 3 controller di vista a pila.

Problemi correlati