22

我在应用商店中有一个应用,并且正在使用日志服务来获取崩溃日志和相关的日志数据。我看到了间歇性崩溃(受影响的用户数量少,每个用户的崩溃数量少),但这让我感到困惑。

这些崩溃中发生的情况如下:

  1. 应用程序启动并初始化核心数据堆栈

  2. 应用程序尝试使用以下代码将 SQL 存储添加到 NSPersistentStoreCoordinator(storeURL有效):

    NSDictionary *options = @{
        NSMigratePersistentStoresAutomaticallyOption : @(YES),
        NSInferMappingModelAutomaticallyOption : @(YES)
    };
    
    sqlStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                         configuration:nil
                                                                   URL:storeURL
                                                               options:options
                                                             error:&error];
    
  3. 添加此商店时出现以下错误之一:

NS错误:

Domain=NSCocoaErrorDomain
Code=256 “操作无法完成。(Cocoa 错误 256。)”
UserInfo=0x1dd946a0 {NSUnderlyingException=授权被拒绝,NSSQLiteErrorDomain=23}

或者

NS错误:

Domain=NSCocoaErrorDomain
Code=256 “操作无法完成。(Cocoa 错误 256。)”
UserInfo=0xc6525d0 {NSUnderlyingException=磁盘 I/O 错误,NSSQLiteErrorDomain=10}

在这种情况下,应用程序将崩溃,因为应用程序需要 SQL 存储才能运行。我可以尝试通过尝试新的 storeURL 来优雅地处理此失败,但我不希望用户丢失现有数据。此外,我从未亲自重现过此问题,并且基于受影响的用户数量和崩溃日志的数量较少,我认为这是一个影响较小的问题,并且不会在后续应用启动时再次出现。

我希望有一位 Core Data 大师就如何调试和预防/处理这些情况提出一些建议。我的核心数据堆栈初始化代码直接来自 xcode 项目生成器,并且我已经排除了任何并发问题,因为持久存储协调器仅初始化一次(在启动时)并且此错误发生在此初始化中。

如果相关,很高兴提供更多代码/信息。

谢谢!

4

4 回答 4

30

看起来像 XJones,我已经能够找到造成这种情况的原因。它似乎是一个 iOS 边缘案例错误或未记录的行为。我已在 Apple 错误 ID 12935031 下提交了此文件。

有一个下落不明的场景,即使用 Core Location 重大位置更改或区域监控的应用程序可能无法正确启动(或产生其他意外后果),因为从 iOS 5 开始,Core Data 存储使用数据保护(加密)默认。

重现步骤:

1) 在设备上开启密码保护

2) 创建一个启动重要位置监控或区域监控的应用程序,即使在后台也能保持启动。IE。使用后台重大位置更改或区域监控的应用程序。

3)等待设备上的电池耗尽(也可能有其他原因)

4) 设备将关闭

5) 将设备连接到 Mac

6) 充电充足后,设备将启动。重要提示:此时不要解锁设备。

7) 退出或进入监控范围或导致位置发生重大变化。该设备现在将自动重新启动应用程序,因为它已注册重要位置监控或区域监控

8) 但是,由于用户尚未解锁设备(尚未输入密码),应用程序将无法读取任何受保护的数据文件。在 Core Data 应用程序中,这将导致持久存储协调器无法将持久存储文件添加到托管对象上下文。这将导致应用程序崩溃或根据开发人员使用的代码甚至尝试重置数据库。在其他应用程序中,它可能会由于其他原因导致崩溃,因为这是 iOS 5 及更高版本中默认为 Core Data 存储打开的数据保护功能的意外、未记录的副作用。

在 Apple 纠正此问题或至少记录它之前,解决方案或解决方法是确保您的应用程序停止监视 applicationWillTerminate 中的重大位置更改/区域,或者通过为选项字典中的 NSFileProtectionKey 键设置 NSFileProtectionNone 来关闭默认数据保护功能将 Core Data 存储添加到持久存储协调器。使用 while() 循环等待文件存储可用,该循环检查受保护的数据是否可用。使用 KVO 可能还有其他方法可以做到这一点,但这种方法工作可靠,并且最容易插入到现有代码中,而无需重新设计整个应用程序启动过程。

更新:如果商店中的数据保护已经处于活动状态,看起来仅仅设置该密钥是不够的。您必须手动设置它:

[[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey] ofItemAtPath:storePath error:nil];

这是需要后台位置监控的应用程序的修复,这要归功于来自 XJones 的更多输入以及对 Apple 分散文档的更多研究:

while(![[UIApplication sharedApplication] isProtectedDataAvailable]) {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5f]];
}

此代码在您尝试将存储数据文件添加到持久存储协调器之前进入。

2015 年更新:您还可以将 NSPersistentStoreFileProtectionKey 设置为 NSFileProtectionNone。这将正确禁用文件保护(如果您不需要它)并且无需任何变通方法即可正常工作。它在以前的尝试中不起作用的原因是因为我正在测试的字典键不正确。

于 2012-12-27T07:15:59.677 回答
4

UIApplication我刚刚注意到文档中的以下通知:

UIApplicationProtectedDataDidBecomeAvailable UIApplicationProtectedDataWillBecomeUnavailable

这些在 iOS 4.0 及更高版本中可用。@lupinglade,我认为您需要观察这些通知,并且只有在收到UIApplicationProtectedDataDidBecomeAvailable.

于 2013-01-29T01:50:08.033 回答
3

解决方案@lupinglade 在我的情况下非常有效(VoIP 应用程序在重新启动时启动),但我还想指出,当受保护的数据可用时调用 AppDelegate 的委托函数:

applicationProtectedDataDidBecomeAvailable:

这使得在我的案例中实施解决方案变得更加容易。

根据头文件,它可以从 iOS 4 开始使用。

于 2013-10-15T12:27:29.800 回答
0

同样的问题在这里,仍然无法找到解决方案。我发现这似乎与在设备上设置了锁码有关。如果没有锁定代码,我永远无法重现错误,现在我已经能够重现很多次了。控制台日志是:

错误是:错误域 = NSCocoaErrorDomain 代码 = 256 “操作无法完成。(可可错误 256。)” UserInfo = 0x1fd80110 {NSUnderlyingException = 授权被拒绝,NSSQLiteErrorDomain = 23}

文件确实存在且未使用任何加密。

于 2012-12-23T20:12:33.150 回答