2012-01-07 101 views
17

所以我在Crashlytics中發現了這個崩潰,在運行iOS 5的iPad和iPad 2上都很常見。它看起來像是由內存警告引起的,但堆棧跟蹤不會「 t引用任何我的應用程序代碼,只是iOS框架:UIViewController purgeMemoryForReason:在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 

我已經谷歌搜索高低,但無法找到任何解決方案。看起來這是由於過度釋放一個UIViewController實例引起的,但我使用的是ARC,所以我不明白這是怎麼回事。

我不知道如何解決這個問題。我什至不知道哪個UIViewController子類導致問題。我試過在模擬器和設備上重現問題,但我找不到是什麼原因造成的。有沒有人看到過這樣的事情,或者就如何處理這個問題提出建議?

+2

有趣。通常,堆棧上的下一步將是該viewController上的unloadViewIfReloadable調用。正如我們現在看到崩潰,這意味着這種方法甚至還沒有達到,或者我們已經落後於這一步。對於後者,請檢查您的viewDidUnload方法實現。那將是我會考慮的下一步。作爲標準建議,在模擬器上啓用殭屍並觸發內存警告。 – Till 2012-01-07 22:19:10

+0

我會給你一個鏡頭,回覆你。謝謝! – 2012-01-07 22:49:51

回答

17

我想我已經解決了這個問題。我在想它,問題不在於卸載UIViewController視圖,而是發佈實際的低內存警告通知。在我的代碼中有幾個實例,我稱之爲[[NSNotificationCenter defaultCenter] removeObserver:self]。這在dealloc方法中很好,但在viewDidUnload方法中有兩個這樣的實例。

我注意到這一點,當我的一個UIViewController的didReceiveMemory斷點沒有被擊中。 viewDidUnload中的代碼也從其他系統通知中取消註冊self,詳情請參閱here

我不打算將此標記爲接受的答案,直到我驗證崩潰停止與新的更新。

更新:我已通過Crashlytics驗證問題已修復!

+2

是的,通知觀察的黃金法則:刪除觀察員只爲您已註冊的通知。我前段時間已經讀過它,但從未見過如此明顯的後果。很高興你解決了你的問題。 – iHunter 2012-01-08 08:36:15

+0

@AshFurrow如果您正在爲viewDidLoad中的那些人註冊,那麼將自己從viewDidUnload _only_上的這些通知中移除。 – Till 2012-01-08 09:52:41

+0

@問題是,UIViewController似乎正在爲在viewDidUnload中註銷的初始化註冊通知。我已經改變了我的removeObserver:removeObserver:name:object:這樣我只* *註銷特定的通知。 – 2012-01-08 15:28:13

8

我注意到,在通過HockeyApp的設備崩潰報告完全相同的堆棧跟蹤在iOS 5上運行

never called[[NSNotificationCenter defaultCenter] removeObserver:self]除了內部的dealloc,所以這不可能是飛機墜毀的原因。

下面是我如何能夠重現崩潰:從MasterViewController我推DetailViewController,然後點擊後退按鈕彈出它。最後,我觸發了內存警告併發生崩潰(僅在iOS 5上)。

事實證明,該DetailViewController實例未使用SVPullToRefresh時被彈出,因爲保留週期後釋放:

@implementation DetailViewController 

- (void) viewDidLoad 
{ 
    [super viewDidLoad]; 

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

@end 

由於DetailViewController不釋放它仍然註冊爲內存警告通知,這是什麼發生:

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 

或者用英語:SVPullToRefreshView實例由於視圖被卸載而被釋放。由於SVPullToRefreshView實例是持有對DetailViewController的引用的最後一個對象,因此它將被釋放,然後釋放。但purgeMemoryForReason:仍然是處理(即訪問實例變量)與剛剛釋放的視圖控制器,因此崩潰。

一旦診斷出解決方案非常簡單:首先避免保留週期。

@implementation DetailViewController 

- (void) viewDidLoad 
{ 
    [super viewDidLoad]; 

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

@end 
+0

這裏的一個錯誤是purgeMemoryForReason在它完成它的工作之前不保留自己,然後在它完成之後釋放自己。另一個原因是,風險投資公司與其觀點沒有很強的聯繫,依靠假定的順序來成功拆除視圖層次結構。 – 2013-09-19 23:10:11

相關問題