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

时间:2012-10-11 18:02:36

标签: ios core-data nspersistentstore

我在应用程序商店中有一个应用程序,并且正在使用日志记录服务来获取崩溃日志和关联的日志数据。我看到一个间歇性的崩溃(受影响的用户数低,每个用户的崩溃次数低),但这让我很困惑。

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

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

  2. App尝试使用以下代码向{NSPersistentStoreCoordinator>添加SQL存储(storeURL有效):

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

  4.   

    NSError:

         
        

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

      

      

    NSError:

         
        

    域= NSCocoaErrorDomain
        代码= 256“操作无法完成。(可可错误256.)”
        UserInfo = 0xc6525d0 {NSUnderlyingException =磁盘I / O错误,NSSQLiteErrorDomain = 10}

      

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

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

    如果相关,请尽快提供更多代码/信息。

    谢谢!

4 个答案:

答案 0 :(得分:27)

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

由于iOS 5中核心数据存储使用数据保护这一事实导致使用核心位置重要位置更改或区域监控的应用程序无法正常启动(或产生其他意外后果),因此存在一个未解决的情况(加密)默认情况下。

重现步骤:

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];

[[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。这将正确禁用文件保护(如果您不需要它),只需工作而无需任何解决方法。之前尝试不起作用的原因是因为我测试的词典键不正确。

答案 1 :(得分:3)

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

UIApplicationProtectedDataDidBecomeAvailable UIApplicationProtectedDataWillBecomeUnavailable

这些在iOS 4.0及更高版本中可用。 @lupinglade,我认为您需要观察这些通知,并且只有在收到UIApplicationProtectedDataDidBecomeAvailable后才能访问受保护的文件(即商店)。

答案 2 :(得分:3)

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

applicationProtectedDataDidBecomeAvailable:

这使得在我的案例中实现解决方案变得容易一些。

根据头文件,它可以从iOS 4开始提供。

答案 3 :(得分:0)

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

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

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