iOS应用程序在恢复时崩溃

(见底部更新)

最近我开始从后台返回时,我的iPhone应用程序奇怪和罕见的崩溃。 崩溃日志只包含系统调用:

Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x00000138 Crashed Thread: 0 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: 0 libobjc.A.dylib 0x34c715b0 objc_msgSend + 16 1 CoreFoundation 0x368b7034 _CFXNotificationPost + 1424 2 Foundation 0x34379d8c -[NSNotificationCenter postNotificationName:object:userInfo:] + 68 3 UIKit 0x37ddfec2 -[UIApplication _handleApplicationResumeEvent:] + 1290 4 UIKit 0x37c37d5c -[UIApplication handleEvent:withNewEvent:] + 1288 5 UIKit 0x37c376d0 -[UIApplication sendEvent:] + 68 6 UIKit 0x37c3711e _UIApplicationHandleEvent + 6150 7 GraphicsServices 0x36dea5a0 _PurpleEventCallback + 588 8 CoreFoundation 0x3693b680 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 12 9 CoreFoundation 0x3693aee4 __CFRunLoopDoSources0 + 208 10 CoreFoundation 0x36939cb2 __CFRunLoopRun + 642 11 CoreFoundation 0x368aceb8 CFRunLoopRunSpecific + 352 12 CoreFoundation 0x368acd44 CFRunLoopRunInMode + 100 13 GraphicsServices 0x36de92e6 GSEventRunModal + 70 14 UIKit 0x37c8b2fc UIApplicationMain + 1116 15 [MyAppName] 0x00083d60 main (main.m:20) 16 [MyAppName] 0x00080304 start + 36 

这可能看起来像是在UIApplicationWillEnterForegroundNotificationUIApplicationDidBecomeActiveNotification上调用的僵尸对象(通过堆栈跟踪中的_handleApplicationResumeEvent进行猜测以及崩溃的时间),但是:

  1. 没有我的类注册UIApplicationDidBecomeActiveNotification ,只有几个单身(永远活着)注册UIApplicationWillEnterForegroundNotification ;
  2. 我做了一些试验,结果发现UIApplicationWillEnterForegroundNotification来自[UIApplication _sendWillEnterForegroundCallbacks:] ,它不在崩溃日志中。

对我来说,这一切都意味着我正在使用的某个库中的一个错误,或者一个系统错误,iOS 5.1.1(发行版),iOS 6.0(发行版)和iOS 6.0debugging版本)。 我扫描了我正在使用的每个库,并有权访问源代码,而且它们既没有注册UIApplicationWillEnterForegroundNotification也没有注册UIApplicationDidBecomeActiveNotification 。 我唯一无法访问的库是TestFlight,但是1.0和1.1版本的TestFlight都发生了崩溃,而且我已经使用了前者很长一段时间了,没有这样的问题。 所以,总结一下,我不知道为什么会出现这个崩溃,它是从哪里来的。 有任何想法吗?

更新1

我已经深入调查了这个问题,感谢DarthMike和Matt的帮助。 通过使用通知中心callback和logging堆栈跟踪,我发现,只有当UIApplicationResumedNotification通知作为从后台返回的一部分被触发时,才会出现这个确切的堆栈。 猜猜看是什么 – 这是一些“私人”的通知,它没有一个公共标识符对应。 它没有userInfo ,它的对象是UIApplication (和之前发布的许多其他通知一样)。 显然我不使用它,也没有任何我有源代码的库。 我在互联网上甚至找不到任何合理的提及! 我也非常怀疑TestFlight是罪魁祸首,因为在debugging过程中也发生了崩溃,而且我没有在debugging模式下“脱机”TestFlight。

这里是接收UIApplicationResumedNotification的堆栈跟踪。 偏移量都是相同的,但有一个固定的字节偏移量(2或4,取决于库 – 可能是因为它是一个debugging堆栈跟踪,而不是释放):

 0 [MyAppName] 0x0016f509 NotificationsCallback + 72 1 CoreFoundation 0x3598ce25 __CFNotificationCenterAddObserver_block_invoke_0 + 124 2 CoreFoundation 0x35911037 _CFXNotificationPost + 1426 3 Foundation 0x333d3d91 -[NSNotificationCenter postNotificationName:object:userInfo:] + 72 4 UIKit 0x36e39ec7 -[UIApplication _handleApplicationResumeEvent:] + 1294 5 UIKit 0x36c91d61 -[UIApplication handleEvent:withNewEvent:] + 1292 6 UIKit 0x36c916d5 -[UIApplication sendEvent:] + 72 7 UIKit 0x36c91123 _UIApplicationHandleEvent + 6154 8 GraphicsServices 0x35e445a3 _PurpleEventCallback + 590 9 CoreFoundation 0x35995683 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14 10 CoreFoundation 0x35994ee9 __CFRunLoopDoSources0 + 212 11 CoreFoundation 0x35993cb7 __CFRunLoopRun + 646 12 CoreFoundation 0x35906ebd CFRunLoopRunSpecific + 356 13 CoreFoundation 0x35906d49 CFRunLoopRunInMode + 104 14 GraphicsServices 0x35e432eb GSEventRunModal + 74 15 UIKit 0x36ce5301 UIApplicationMain + 1120 16 [MyAppName] 0x000aa603 main + 390 17 [MyAppName] 0x000a41b0 start + 40 

NotificationsCallback是一个“观察者”callback,我现在已经添加了debugging。

为了certificate一个观点,我故意省略了一个removeObserver:来自我的一个对象的调用来生成一个僵尸/exception,并且堆栈跟踪仍然包含_CFXNotificationPost + 1426随后在objc_msgSend + 16EXC_BAD_ACCESS一起崩溃,就像在我的原来的崩溃。 所以这就意味着有人已经注册了UIApplicationResumedNotification的观察者,并且在观察者被释放之前没有移除它。 基于我从来没有注册过这样的通知,我可以认为这次崩溃不是我的错。 问题仍然存在 – 那么谁的呢? 我想知道是谁实际上注册了这个通知

更新2

虽然我仍然在等待,看看在我的应用程序的新版本上这个bug是否有任何改变,但是由于这个原因导致了以前版本的再次崩溃。 事实certificate,无论是UIApplicationResumedNotification寄存器,都为它指定了select器_applicationResuming: 我怀疑这是什么用途。

我刚刚遇到了这个问题,并find了一个解决scheme,不涉及删除通知。 在我们的例子中,有这样的旧代码:

 - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { [searchBar resignFirstResponder]; // other stuff } 

我不知道我们为什么有这个,但现在已经消失了,事故已经没有了。 看起来在这种情况下,当searchBarTextDidBeginEditing被调用时,辞职的第一响应者孤儿在search栏的文本编辑字段上发出通知,然后一旦拥有这个UISearchBar的视图控制器被释放,我们就会崩溃,并且我们做了背景/前景舞蹈。

因人而异

我在运行IOS 6.0.1的设备的崩溃报告中有完全相同的堆栈跟踪。 我设法通过以下模式在模拟器上重现问题:

  • 把应用程序放在后台
  • 模拟模拟器菜单中的内存警告
  • 把应用程序带回前台

经过大量的debugging,我发现_applicationResuming:消息被发送到UITextField ,我正在释放内存警告的反应。 我在IOS 5.1下testing了相同的模式,但并没有导致崩溃。 出于某种原因在IOS 6 UITextField注册ApplicationResumeEvent(可能不总是,但键盘出现后)。
我的解决方法是在释放它之前从NSNotificationCenter中删除这个对象:

 [[NSNotificationCenter defaultCenter] removeObserver:self.placeFld]; self.placeFld = nil; 

把断点放在-[NSNotificationCenter postNotificationName:object:userInfo:] 。 它试图发送一个通知给一个不存在的对象,或类似的东西。 您可能会错误地pipe理自己的通知或自己的对象。

如果您尚未使用,请考虑切换到ARC。

使用静态分析器。 它可以find潜在的记忆问题。

可能是很多事情,但我认为检查注册了任何UIApplication通知的代码会更好。 你不知道哪个通知触发错误。

另外,任何对象保留/持有强大的AppDelegate引用? 这可能会导致一些怪异的保留周期,使这种崩溃发生。

我从来没有见过XCode的这种不正当行为。

编辑:从头文件粘贴所有通知。 可能是矫枉过正,但有些可能会在应用程序简历/从后台发送

 UIKIT_EXTERN NSString *const UIApplicationDidEnterBackgroundNotification NS_AVAILABLE_IOS(4_0); UIKIT_EXTERN NSString *const UIApplicationWillEnterForegroundNotification NS_AVAILABLE_IOS(4_0); UIKIT_EXTERN NSString *const UIApplicationDidFinishLaunchingNotification; UIKIT_EXTERN NSString *const UIApplicationDidBecomeActiveNotification; UIKIT_EXTERN NSString *const UIApplicationWillResignActiveNotification; UIKIT_EXTERN NSString *const UIApplicationDidReceiveMemoryWarningNotification; UIKIT_EXTERN NSString *const UIApplicationWillTerminateNotification; UIKIT_EXTERN NSString *const UIApplicationSignificantTimeChangeNotification; UIKIT_EXTERN NSString *const UIApplicationWillChangeStatusBarOrientationNotification; // userInfo contains NSNumber with new orientation UIKIT_EXTERN NSString *const UIApplicationDidChangeStatusBarOrientationNotification; // userInfo contains NSNumber with old orientation UIKIT_EXTERN NSString *const UIApplicationStatusBarOrientationUserInfoKey; // userInfo dictionary key for status bar orientation UIKIT_EXTERN NSString *const UIApplicationWillChangeStatusBarFrameNotification; // userInfo contains NSValue with new frame UIKIT_EXTERN NSString *const UIApplicationDidChangeStatusBarFrameNotification; // userInfo contains NSValue with old frame UIKIT_EXTERN NSString *const UIApplicationStatusBarFrameUserInfoKey; // userInfo dictionary key for status bar frame UIKIT_EXTERN NSString *const UIApplicationLaunchOptionsURLKey NS_AVAILABLE_IOS(3_0); // userInfo contains NSURL with launch URL UIKIT_EXTERN NSString *const UIApplicationLaunchOptionsSourceApplicationKey NS_AVAILABLE_IOS(3_0); // userInfo contains NSString with launch app bundle ID UIKIT_EXTERN NSString *const UIApplicationLaunchOptionsRemoteNotificationKey NS_AVAILABLE_IOS(3_0); // userInfo contains NSDictionary with payload UIKIT_EXTERN NSString *const UIApplicationLaunchOptionsLocalNotificationKey NS_AVAILABLE_IOS(4_0); // userInfo contains a UILocalNotification UIKIT_EXTERN NSString *const UIApplicationLaunchOptionsAnnotationKey NS_AVAILABLE_IOS(3_2); // userInfo contains object with annotation property list UIKIT_EXTERN NSString *const UIApplicationProtectedDataWillBecomeUnavailable NS_AVAILABLE_IOS(4_0); UIKIT_EXTERN NSString *const UIApplicationProtectedDataDidBecomeAvailable NS_AVAILABLE_IOS(4_0); UIKIT_EXTERN NSString *const UIApplicationLaunchOptionsLocationKey NS_AVAILABLE_IOS(4_0); // app was launched in response to a CoreLocation event. UIKIT_EXTERN NSString *const UIApplicationLaunchOptionsNewsstandDownloadsKey NS_AVAILABLE_IOS(5_0); // userInfo contains an NSArray of NKAssetDownlo 

在debugging某个奇怪的崩溃之前,我需要做一些事情来清除卡组。

构build干净(摆脱本地caching的文件)。

从Simulator / Device中删除应用程序(有时XIB被caching)。

重新启动Xcode(当Xcode与当前设置不同步时,会出现一些奇怪的错误)。

然后,再试一次。