在将SQL存储添加到NSPersistentStoreCoordinator时,如何调试/处理间歇性的"授权被拒绝"和"磁盘i/o"错误?

XJo*_*nes 21 core-data ios nspersistentstore

我在应用程序商店中有一个应用程序,并使用日志服务来获取崩溃日志和相关的日志数据.我看到一个间歇性的崩溃(受影响的用户数低,每个用户的崩溃次数低),但令我感到困惑.

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

  1. 应用程序启动并初始化Core Data堆栈

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

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

NSError:

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

要么

NSError:

Domain = NSCocoaErrorDomain
Code = 256"无法完成操作.(Cocoa error 256.)"
UserInfo = 0xc6525d0 {NSUnderlyingException = disk I/O error,NSSQLiteErrorDomain = 10}

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

我希望那里有一位核心数据专家,提供一些关于如何调试和预防/处理这些条件的建议.我的核心数据堆栈初始化代码直接来自xcode项目生成器,我排除了任何并发问题,因为持久性存储协调器只初始化一次(启动时),并且此错误发生在此初始化中.

很高兴提供更多相关的代码/信息.

谢谢!

lup*_*ade 28

它看起来像XJones和我已经能够找到原因.它似乎是一个iOS边缘案例错误或无证件行为.我已经在Apple Bug ID 12935031下提交了此文件.

由于从iOS 5开始,Core Data存储使用数据保护(加密),因此使用Core Location重要位置更改或区域监视的应用程序无法正常启动(或产生其他意外后果)的情况有一个未解决的情况默认.

重现步骤:

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];
Run Code Online (Sandbox Code Playgroud)

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

[[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey] ofItemAtPath:storePath error:nil];
Run Code Online (Sandbox Code Playgroud)

以下是需要进行背景位置监控的应用程序的修复程序,这得益于XJones的更多输入以及Apple分散的文档中的更多研究:

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

在您尝试将商店数据文件添加到持久性存储协调器之前,此代码会进入.

更新2015:您还可以将NSPersistentStoreFileProtectionKey设置为NSFileProtectionNone.这将正确禁用文件保护(如果您不需要它),只需工作而无需任何解决方法.它在之前的尝试中不起作用的原因是因为我测试的字典键不正确.