【问题标题】:iOS app crashes on resumingiOS 应用程序在恢复时崩溃
【发布时间】:2012-09-26 12:08:35
【问题描述】:

(见底部更新)

最近,当我的 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.0(调试版本)。我扫描了我正在使用的每个库并可以访问其源代码,但它们既没有注册 UIApplicationWillEnterForegroundNotification 也没有注册 UIApplicationDidBecomeActiveNotification。我唯一无法访问的库是 TestFlight,但崩溃发生在 TestFlight 的 1.0 和 1.1 版本上,我已经使用前者很长一段时间了,没有出现此类问题。 所以,总而言之,我不知道为什么会出现这种崩溃以及它来自什么。有什么想法吗?

更新 1

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

这是接收UIApplicationResumedNotification 的堆栈跟踪。偏移量都是相同的,但具有恒定的字节偏移量(2 或 4,取决于库 - 可能是因为它是调试堆栈跟踪,而不是发布):

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 是我为调试添加的“观察者”回调。

为了证明一点,我故意从我的一个对象中省略了 removeObserver: 调用以生成僵尸/异常,并且堆栈跟踪仍然包含 _CFXNotificationPost + 1426,然后在 @987654339 中出现 EXC_BAD_ACCESS 崩溃@,就像我最初的崩溃一样。 所以这只是意味着有人已经为UIApplicationResumedNotification 注册了一个观察者,并且在观察者被解除分配之前没有删除它。基于我从未注册过此类通知的事实,我可以假设这次崩溃不是我的错。问题仍然存在——那是谁呢?我想知道到底是谁注册了这个通知......

更新 2

虽然我仍在等待查看我的应用程序的新版本是否对此错误有任何更改,但我在以前的版本中遇到了另一个由此导致的崩溃。事实证明,无论为UIApplicationResumedNotification 注册什么,都会为其指定选择器_applicationResuming:。不过我怀疑这有什么用。

【问题讨论】:

  • “对我来说,所有这些都意味着我正在使用的某个库中存在错误,或者是系统错误。”我将给你最好的 Cocoa touch 编程建议。假设是
  • 如果您使用的是 Localytics,那么这是他们图书馆的一个已知问题,他们已经为他们的图书馆发送了紧急更新
  • 谢谢,Leftertis,但我不使用 Localytics,我使用 TestFlight。
  • 对于这种错误,我通常会这样处理:删除应用程序的部分直到不会发生崩溃。您是否可以/容易地删除 TestFlight 的使用,或删除您正在使用的其他库,甚至删除代码中的 addObserver 调用?一个一个地删除应该会发现问题。如果是框架问题,那么如果没有任何对象注册任何通知,就会发生崩溃。
  • 达斯迈克,我认为这不可能。有大量代码和几个库,我通过 TestFlight 收到崩溃,所以我需要它。这种崩溃非常很少发生,所以——即使我再也看不到它,也不意味着它不存在……我想暂时,我会忽略它并等待如果它再次发生。

标签: objective-c ios crash


【解决方案1】:

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

  • 将应用程序置于后台
  • 从模拟器菜单模拟内存警告
  • 让应用程序回到前台

经过大量调试后,我发现 _applicationResuming: 消息已发送到 UITextField,我将其作为对内存警告的反应而发布。我在 IOS 5.1 下测试了相同的模式,但它没有导致崩溃。出于某种原因,在 IOS 6 UITextField 中为 ApplicationResumeEvent 注册了(可能并非总是如此,而是在键盘出现之后)。
我的解决方法是在释放它之前从 NSNotificationCenter 中删除它:

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

【讨论】:

  • 哇,如果我能在我的情况下证实这一点,对于仍然认为这是我的错在我的应用程序中发生的每个人来说,这将是一个重大的“当面”。由于这个问题,我仍然报告了罕见的崩溃,所以我会尝试按照你描述的方式重现它。不过,我已经转移到另一个项目,所以可能需要一些时间......还是谢谢你!
  • 我不得不在 -[UIApplication _handleApplicationResumeEvent:] 和 __CFNotificationCenterAddObserver_block_invoke_0 中使用汇编代码中的断点进行硬调试会话。然后我可以使用 $ecx 寄存器中的地址检查接收通知的对象。我在没有模拟内存警告的情况下进行了这样的会话,并获取了接收通知的对象列表(po $ecx 将为您提供 UITextField 的内容,从而使识别更加容易)。然后用内存警告重复会话。对象以相同的顺序接收通知。
【解决方案2】:

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

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

我不知道我们为什么会有这个,但现在它已经消失了,崩溃也消失了。似乎在这种情况下,在调用 searchBarTextDidBeginEditing 时辞职第一响应者会孤立搜索栏的文本编辑字段上的通知,然后一旦拥有此 UISearchBar 的视图控制器被释放并且我们执行了背景/前景,我们就会崩溃跳舞。

YMMV

【讨论】:

  • 确实,在我的应用程序中有几个地方可以做几乎相同的事情!我尝试在 iOS 6.0 模拟器上重现相同的场景 - 结果确实如此!很遗憾我不再在这个应用程序上工作,但仍然值得了解这个错误,所以谢谢!我肯定会在谷歌上搜索这个错误是否已经被记录,如果没有 - 我会发布错误报告。
  • 我还应该在这里注意,我必须实施 searchBarShouldBeginEditing 并在搜索栏应该保持不变的情况下返回 NO,而不是尝试在 searchBarTextDidBeginEditing 中辞去第一响应者的职务。虽然我无法在 searchBarTextDidBeginEditing 中调用 resignFirstResponder 的任何地方都没有记录,但是对于简单的 UITextField,同样的技巧可以正常工作。
  • 顺便说一句,我可以确认这个问题已在我们最喜欢的移动操作系统的最新版本中得到修复,由于 NDA 的原因我可能无法提及 :)
【解决方案3】:

-[NSNotificationCenter postNotificationName:object:userInfo:] 上放置一个断点。它试图向不再存在的对象或类似的对象发送通知。您可能对自己的通知或自己的对象管理不善。

如果您还没有使用 ARC,请考虑切换到 ARC。

使用静态分析器。它可以发现潜在的内存问题。

【讨论】:

  • 谢谢,我使用了断点和“通配符”观察者回调来通过堆栈跟踪日志记录所有生成的通知,请查看我的问题的更新结果。是的,我已经在使用 ARC 并定期运行静态分析器。提到我正在开发的这个应用程序已经有将近一年的历史了,而且我已经看到了许多类型的崩溃并修复了它们,这可能很有用...
【解决方案4】:

可能有很多事情,但我认为检查注册任何 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

【讨论】:

  • 谢谢,真的值得一试!顺便说一句,我怀疑UIApplicationWillChangeStatusBarFrameNotification...很快就会调查。
【解决方案5】:

在调试一些奇怪的崩溃之前,我会做一些事情来清理甲板。

Build Clean(摆脱本地缓存文件)。

从模拟器/设备中删除应用程序(有时会缓存 XIB)。

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

然后,再试一次。

【讨论】:

  • 谢谢,但是这个问题在调试和发布(App Store 和 Ad-hoc)配置中都存在,而且我已经完成了几次清理,所以我想这不是原因.
猜你喜欢
  • 2015-09-02
  • 1970-01-01
  • 1970-01-01
  • 2016-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
相关资源
最近更新 更多