将SQL存储添加到NSPersistentStoreCoordinator时如何debugging/处理间歇性的“授权被拒绝”和“磁盘I / O”错误?

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

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

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

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

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

NSError:

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

要么

NSError:

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

在这种情况之后,应用程序将崩溃B / C SQL应用程序所需的function。 我可以通过尝试一个新的storeURL来优雅地处理这个失败,但我不希望用户丢失现有的数据。 此外,我从来没有亲自转载这个问题,并基于受影响的用户和崩溃日志的数量较低,我认为这是一个低影响的问题,并不会在随后的应用程序启动发生。

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

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

谢谢!

它看起来像XJones,我已经能够find这个原因。 这似乎是一个iOS边缘案例或无证行为。 我已经提交了这个苹果bug ID 12935031。

有一个下落不明的情况下,使用核心位置重大位置更改或区域监视的应用程序可能无法正常启动(或有其他意想不到的后果),因为从iOS 5开始,核心数据存储使用数据保护(encryption)默认。

重现步骤:

1)打开设备上的密码保护

2)创build一个启动重要位置监控或区域监控的应用程序,即使在后台也可以启动。 IE浏览器。 使用背景显着位置更改或区域监控的应用程序。

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

4)设备将closures

5)将设备连接到Mac

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

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

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

解决方法或解决方法,直到苹果纠正这个问题或至less文件是要确保您的应用程序停止监视applicationWillTerminate中的重大位置更改/区域或closures默认的数据保护function,通过设置NSFileProtectionNone NSFileProtectionKey键在选项字典中时将Core Data存储添加到持久存储协调器。 使用while()循环等待文件存储变得可用,该循环检查受保护的数据是否可用。 使用KVO可能有其他的方法,但是这种方法可靠地工作,并且最容易插入到现有的代码中,而不需要重新整理应用程序的启动过程。

更新:看起来像只是设置该密钥是不够的,如果数据保护已经在商店活跃。 你必须手动设置它:

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

以下是需要进行背景位置监控的应用程序的修复方法,这要感谢XJones提供的更多信息以及对苹果分散文档的更多研究:

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

在您尝试将商店数据文件添加到持久性商店协调员之前,此代码已进入。

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

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

UIApplicationProtectedDataDidBecomeAvailable UIApplicationProtectedDataWillBecomeUnavailable

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

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

 applicationProtectedDataDidBecomeAvailable: 

这使得我的案例更容易实施解决scheme。

根据头文件可以从iOS 4开始。

同样的问题在这里,还没有find解决办法。 我发现这似乎与在设备上设置锁码有关。 无需锁代码我永远不能重现错误,现在我已经能够一堆。 控制台日志是:

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

文件确实存在,不使用任何encryption。