2012-01-07 18 views
17

Quindi ho visto questo crash piuttosto frequentemente in Crashlytics, sia su iPad che su iPad 2, con iOS 5. Sembra che sia causato da un avviso di memoria, ma la traccia dello stack non lo fa t di riferimento qualsiasi del mio codice dell'applicazione, a soli framework iOS:UIViewController purgeMemoryForReason: Crashing su iOS 5

0 libobjc.A.dylib objc_msgSend + 15 
1 UIKit   -[UIViewController purgeMemoryForReason:] + 64 
2 Foundation  __57-[NSNotificationCenter addObserver: selector: name: object:]_block_invoke_0 + 18 
3 CoreFoundation  ___CFXNotificationPost_block_invoke_0 + 70 
4 CoreFoundation  _CFXNotificationPost + 1406 
5 Foundation  -[NSNotificationCenter postNotificationName: object: userInfo:] + 66 
6 Foundation  -[NSNotificationCenter postNotificationName: object:] + 30 
7 UIKit   -[UIApplication _performMemoryWarning] + 80 
8 UIKit   -[UIApplication _receivedMemoryNotification] + 174 
9 libdispatch.dylib _dispatch_source_invoke + 516 
10 libdispatch.dylib _dispatch_queue_invoke + 50 
11 libdispatch.dylib _dispatch_main_queue_callback_4CF + 156 
12 CoreFoundation  __CFRunLoopRun + 1268 
13 CoreFoundation  CFRunLoopRunSpecific + 300 
14 CoreFoundation  CFRunLoopRunInMode + 104 
15 GraphicsServices GSEventRunModal + 156 
16 UIKit   UIApplicationMain + 1090 
17 500px iOS  main.m line 12 

googled alta e bassa, ma non riesco a trovare alcuna soluzione a questo. Sembra che questo sia causato dal rilascio eccessivo di un'istanza di UIViewController, ma sto usando ARC, quindi non vedo come potrebbe essere il caso.

Sono a corto di come avvicinarmi anche a questo. Non riesco nemmeno a capire quale sottoclasse di UIViewController stia causando il problema. Ho provato a riprodurre il problema nel simulatore e sul dispositivo, ma non riesco a trovare ciò che lo causa. Qualcuno ha mai visto qualcosa di simile o ha suggerimenti su come affrontare la riproduzione del problema?

+2

Interessante. Di solito, il passo successivo nello stack è una chiamata a downloadViewIfReloadable su quel viewController. Come vediamo ora un incidente, questo significa che questo metodo non è stato nemmeno raggiunto o che abbiamo già ottenuto questo passo. Per quest'ultimo, controlla le implementazioni del metodo viewDidUnload. Questo sarebbe il prossimo passo che prenderei in considerazione. Come raccomandazione standard, abilita gli zombi e attiva l'avviso di memoria sul simulatore. – Till

+0

Darò uno scatto e tornerò da te. Grazie! –

risposta

17

Penso di aver risolto il problema. Ci stavo pensando, e il problema non è lo scarico della vista di UIViewController, è la pubblicazione della notifica di avviso di memoria bassa effettiva. Ci sono diverse istanze nel mio codice in cui chiamo [[NSNotificationCenter defaultCenter] removeObserver:self]. Questo va bene nel metodo dealloc, ma c'erano due casi nei metodi viewDidUnload.

Ho notato questo quando il mio punto di interruzione in didReceiveMemory di uno di UIViewController non è stato colpito. Il codice viewDidUnload annullava inoltre la registrazione di self da altre notifiche di sistema, come dettagliato here.

Non ho intenzione di contrassegnarlo come una risposta accettata finché non verificherò che gli arresti anomali si interrompono con il nuovo aggiornamento.

AGGIORNAMENTO: Ho verificato con Crashlytics che il problema è stato risolto!

+2

Sì, la regola d'oro dell'osservazione delle notifiche: rimuovi solo gli osservatori per le notifiche per cui ti sei registrato. Ne ho letto qualche tempo fa, ma non ho mai visto le conseguenze così chiare. Sono contento che hai risolto il problema. – iHunter

+0

@AshFurrow rimuovi sé stesso da quelle notifiche su viewDidUnload _lyly_ se ti stai registrando per quelli in viewDidLoad. – Till

+0

@Per il problema, UIViewController sembra essere registrato per le notifiche sull'inizializzazione che sto annullando la registrazione in viewDidUnload. Ho cambiato il mio removeObserver: to removeObserver: name: object: in modo che io * registri * solo * per notifiche specifiche. –

8

ho notato la stessa identica analisi dello stack in incidenti riportati da HockeyApp per i dispositivi in ​​esecuzione su iOS 5.

ho never called[[NSNotificationCenter defaultCenter] removeObserver:self] tranne dealloc all'interno, quindi questo non potrebbe essere la causa dello schianto.

Ecco come sono riuscito a riprodurre lo schianto: da MasterViewController premere DetailViewController, quindi farlo scattare toccando il pulsante Indietro. Infine, faccio scattare un avviso di memoria e l'arresto si verifica (solo su iOS 5).

Si scopre che l'istanza DetailViewController non viene rilasciato dopo essere spuntato a causa di un ciclo di mantenere quando si utilizza SVPullToRefresh:

@implementation DetailViewController 

- (void) viewDidLoad 
{ 
    [super viewDidLoad]; 

    [self.scrollView addPullToRefreshWithActionHandler:^{ 
     [self refresh]; 
    }]; 
} 

@end 

Dal momento che il DetailViewController non viene rilasciato è ancora registrato per le notifiche di avviso di memoria e questo è ciò che accade:

frame #0: 0x0004d61b MyApp`-[DetailViewController dealloc](self=0x089a5150, _cmd=0x024d2738) + 27 at DetailViewController.m:103 
frame #1: 0x0227ae3d libobjc.A.dylib`_objc_rootRelease + 47 
frame #2: 0x0227ae00 libobjc.A.dylib`objc_release + 48 
frame #3: 0x0227c047 libobjc.A.dylib`objc_storeStrong + 39 
frame #4: 0x0004e44c MyApp`__destroy_helper_block_ + 44 at DetailViewController.m:157 
frame #5: 0x029b555d libsystem_sim_blocks.dylib`_Block_release + 166 
frame #6: 0x0227ae00 libobjc.A.dylib`objc_release + 48 
frame #7: 0x0227c047 libobjc.A.dylib`objc_storeStrong + 39 
frame #8: 0x00084c8d MyApp`-[SVPullToRefreshView .cxx_destruct](self=0x08bf3af0, _cmd=0x00000001) + 525 at UIScrollView+SVPullToRefresh.m:121 
frame #9: 0x0226630d libobjc.A.dylib`object_cxxDestructFromClass + 104 
frame #10: 0x02270fde libobjc.A.dylib`objc_destructInstance + 38 
frame #11: 0x02271015 libobjc.A.dylib`object_dispose + 20 
frame #12: 0x0247a9a1 CoreFoundation`-[NSObject dealloc] + 97 
frame #13: 0x00a8cdc7 UIKit`-[UIView dealloc] + 748 
frame #14: 0x0227ae3d libobjc.A.dylib`_objc_rootRelease + 47 
frame #15: 0x00a90b73 UIKit`-[UIView(Hierarchy) removeFromSuperview] + 194 
frame #16: 0x00a8cc10 UIKit`-[UIView dealloc] + 309 
frame #17: 0x00a9d6ff UIKit`-[UIScrollView dealloc] + 405 
frame #18: 0x013ab36c Foundation`NSKVODeallocate + 105 
frame #19: 0x0227ae3d libobjc.A.dylib`_objc_rootRelease + 47 
frame #20: 0x00b21c12 UIKit`-[UIViewController setView:] + 447 
frame #21: 0x00b21885 UIKit`-[UIViewController unloadViewForced:] + 117 
frame #22: 0x00b2180b UIKit`-[UIViewController unloadViewIfReloadable] + 41 
frame #23: 0x00b256ff UIKit`-[UIViewController purgeMemoryForReason:] + 75 
frame #24: 0x00b2563b UIKit`-[UIViewController didReceiveMemoryWarning] + 41 
frame #25: 0x00b2560d UIKit`-[UIViewController _didReceiveMemoryWarning:] + 33 
frame #26: 0x0141ca29 Foundation`__57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 40 
frame #27: 0x02443855 CoreFoundation`___CFXNotificationPost_block_invoke_0 + 85 
frame #28: 0x02443778 CoreFoundation`_CFXNotificationPost + 1976 
frame #29: 0x0136119a Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 98 
frame #30: 0x0136db03 Foundation`-[NSNotificationCenter postNotificationName:object:] + 55 
frame #31: 0x00a64cf4 UIKit`-[UIApplication _performMemoryWarning] + 91 
frame #32: 0x00a64e00 UIKit`-[UIApplication _receivedMemoryNotification] + 180 
frame #33: 0x00a64f98 UIKit`__block_global_0 + 36 
frame #34: 0x029f1450 libdispatch.dylib`_dispatch_source_invoke + 719 
frame #35: 0x029edcc4 libdispatch.dylib`_dispatch_queue_invoke + 66 
frame #36: 0x029ee4cf libdispatch.dylib`_dispatch_main_queue_callback_4CF + 295 
frame #37: 0x023af803 CoreFoundation`__CFRunLoopRun + 2003 
frame #38: 0x023aed84 CoreFoundation`CFRunLoopRunSpecific + 212 
frame #39: 0x023aec9b CoreFoundation`CFRunLoopRunInMode + 123 
frame #40: 0x038d07d8 GraphicsServices`GSEventRunModal + 190 
frame #41: 0x038d088a GraphicsServices`GSEventRun + 103 
frame #42: 0x00a5a626 UIKit`UIApplicationMain + 1163 
frame #43: 0x00002b82 MyApp`main(argc=1, argv=0xbffff318) + 178 at main.m:15 

o in inglese: l'istanza SVPullToRefreshView viene rilasciato a seguito della vista venga scaricato. Poiché l'istanza SVPullToRefreshView è l'ultimo oggetto che contiene un riferimento allo DetailViewController, viene rilasciato e quindi deallocato. Ma era ancora facendo cose (cioè accedere a variabili di istanza) con il controller di vista appena deallocato, quindi il crash.

Una volta diagnosticata la soluzione era molto semplice: basta evitare il ciclo di conservazione in primo luogo.

@implementation DetailViewController 

- (void) viewDidLoad 
{ 
    [super viewDidLoad]; 

    __typeof__(self) __weak weakSelf = self; 
    [self.scrollView addPullToRefreshWithActionHandler:^{ 
     [weakSelf refresh]; 
    }]; 
} 

@end 
+0

Un errore qui è che purgeMemoryForReason non mantiene il sé prima che funzioni e poi rilasci dopo che è finito. Un altro è che il VC non aveva una forte connessione con le sue viste, basandosi sull'ordinamento presunto per un teardown riuscito della gerarchia della vista. –