2015-05-07 10 views
6

Ho un metodo -[tableView reloadData] nella mia app e per rendere l'esecuzione più veloce, l'ho chiamato in un GCD nel seguente metodo.Interrompe l'esecuzione di un GCD, quando il ViewController è spuntato

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 

    [tableView reloadData]; 
});  

Ma quando ho pop mia viewController, l'applicazione si blocca con questo messaggio [SecondViewController numberOfSectionsInTableView:]: message sent to deallocated instance 0x7fe376206e10. Suppongo che il [reloadData] sia ancora in esecuzione dopo che esco da quel ViewController. Come posso interrompere la sua esecuzione? Dovrei farlo diventare una NSOperation? Se sì, come lo farò?

+0

Dove e quando si chiama questo metodo GCD? – Leo

+0

all'interno del mio '- (void) tableView: (UITableView *) tableView willDisplayCell: (UITableViewCell *) cella forRowAtIndexPath: (NSIndexPath *) indexPath' non chiedere perché, è una lunga storia –

+1

C'è un pizzico di codice odore qui, ma come tu ci hai chiesto di non indagare, non lo farò. Ma, in fondo, se vuoi cancellare qualcosa che accade in 1,5 secondi, è probabilmente meglio usare 'NSTimer' (che puoi cancellare facilmente chiamando' invalidate' sull'oggetto timer) piuttosto che 'dispatch_after'. – Rob

risposta

2

Ci sono un paio di problemi con il tuo codice. Ecco la sequenza di eventi che portano al crash

1) Il blocco acquisisce il TableView e lo mantiene attivo.

2) Il controller di vista è quindi deallocata dal pop,

3) l'esecuzione della blocchi e la tableView chiama attraverso ad esso è fonte di dati (il controller di vista) che è ora deallocata.

È possibile risolvere questo fermando il n. 1 o il n. 3 sopra. Suggerirei # 1. (Sto assumendo ARC qui)

__weak UITableView *weakTableView = tableView; 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 
    [weakTableView reloadData]; 
}); 

Se questo non funziona, qualcosa d'altro è probabile che mantenere viva la tableView. Si dovrebbe indagare che cosa esattamente sta facendo questo, ma si può anche risolvere l'incidente, impedendo # 3 dal controller di vista dealloc metodo:

- (void)dealloc { 
    self.tableView.dataSource = nil; 
    self.tableView.delegate = nil; 
} 
+0

'- (void) dealloc' ha funzionato. Dovrei essere preoccupato per qualcosa qui? –

+0

È sempre una buona pratica escludere i delegati/dataSources in dealloc per questo esatto motivo. – Lance

-1

Purtroppo, non si può fermare l'esecuzione GCD, ma c'è un altro modo di correggi questo bug Poiché la domanda principale in questo thread riguarda l'interruzione dell'esecuzione, pubblicherò una soluzione basata su ciò che chiedi, usando NSOperation.

1- Creare un NSOperationQueue

NSOperationQueue *_myQueue; 
_myQueue = [NSOperationQueue new]; 
_myQueue.name = @"com.my.queue"; 

2- Ricarica il tuo tavolo dalla coda. (Userò blocchi ok?)

[_myQueue addOperationWithBlock:^{ 

    //your really expensive function 
    //and your table reload call 
    [tableView reloadData]; 
}]; 

3- Ora è possibile annullare l'esecuzione utilizzando

//maybe you will want to do this on viewDidDisappear 
[_myQueue cancelAllOperations]; 

Aggiornamento:

Ops, ho visto che si stanno ritardando la ricarica da tavolo chiamata, ma NSOperation non ha un meccanismo di ritardo. Per risolvere questo è possibile simulare un ritardo utilizzando

[NSThread sleepForTimeInterval:1.5]; 

prima di chiamare [tableView reloadData]; all'interno addOperationWithBlock: o continuare a utilizzare GCD come si sta facendo in questo momento, e cambiando il riferimento del tableView per weak per evitare il blocco conservando l'oggetto tableView, come questo :

__weak __typeof__(tableView) weakTable = tableView; 

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 

    //Now, if your table object was released at this point, the reloadData 
    //will be ignored 
    [weakTable reloadData]; 
}); 

Spero che aiuti ...

+0

Con questo, l'arresto anomalo è ancora possibile poiché la deallocazione del controller di visualizzazione potrebbe verificarsi dopo l'avvio del blocco ma prima che venga richiamato il reloadData. Anche questo non tiene conto del ritardo che vuole. – Lance

+0

E questo è il motivo per cui dovresti usare '[_myQueue cancelAllOperations];'. – FormigaNinja

0

Come opzione è possibile debolmente mantenere l'origine dati e verificare la presenza di esso:

__weak __typeof__(self) dataSource = self; // or whatever it is 

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 
    if (dataSource!=nil)  
    { 
    [weakTable reloadData]; 
    } 
}); 

C'è ancora un incidente molto improbabile, se si tiene l'origine dei dati, impostare uno nuovo alla vista tavolo e rimuovilo nuovamente, quindi viene deallocato.

Problemi correlati