UIViewController purgeMemoryForReason: Crashing on iOS 5

后端 未结 2 655
梦如初夏
梦如初夏 2020-12-28 18:03

So I\'ve been seeing this crash pretty frequently in Crashlytics, both on iPad and iPad 2, running iOS 5. It looks like it\'s caused by a memory warning, but the stack trace

2条回答
  •  清歌不尽
    2020-12-28 18:18

    I noticed the exact same stack trace in crashes reported by HockeyApp for devices running on iOS 5.

    I never called [[NSNotificationCenter defaultCenter] removeObserver:self] except inside dealloc, so this could not be the cause of the crash.

    Here is how I was able reproduce the crash: from MasterViewController I push DetailViewController, then pop it by tapping the back button. Finally, I trigger a memory warning and the crash happens (on iOS 5 only).

    It turns out that the DetailViewController instance is not released after being popped because of a retain cycle when using SVPullToRefresh:

    @implementation DetailViewController
    
    - (void) viewDidLoad
    {
        [super viewDidLoad];
    
        [self.scrollView addPullToRefreshWithActionHandler:^{
            [self refresh];
        }];
    }
    
    @end
    

    Since the DetailViewController is not released it’s still registered for memory warning notifications and this is what happens:

    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
    

    Or in english: the SVPullToRefreshView instance is released as a result of the view being unloaded. Since the SVPullToRefreshView instance is the last object to hold a reference to the DetailViewController, it is released, then deallocated. But purgeMemoryForReason: was still doing things (i.e. accessing instance variables) with the just deallocated view controller, hence the crash.

    Once diagnosed the solution was very simple: just avoid the retain cycle in the first place.

    @implementation DetailViewController
    
    - (void) viewDidLoad
    {
        [super viewDidLoad];
    
        __typeof__(self) __weak weakSelf = self;
        [self.scrollView addPullToRefreshWithActionHandler:^{
            [weakSelf refresh];
        }];
    }
    
    @end
    

提交回复
热议问题