ios - ¿Cómo depurar/manejar los errores intermitentes de "autorización denegada" y "disco de E/S" al agregar almacenamiento de SQL a NSPersistentStoreCoordinator?
core-data (4)
Tengo una aplicación en la tienda de aplicaciones y estoy usando un servicio de registro para obtener registros de fallas y datos de registro asociados. Estoy viendo un bloqueo intermitente (bajo número de usuarios afectados y bajo número de bloqueos por usuario) pero me desconcierta.
Lo que sucede en estos choques es el siguiente:
La aplicación inicia e inicializa la pila de datos básicos
La aplicación intenta agregar un almacén SQL al NSPersistentStoreCoordinator con el siguiente código (
storeURL
es válido):NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption : @(YES), NSInferMappingModelAutomaticallyOption : @(YES) }; sqlStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error];
Se produce uno de los siguientes errores al agregar esta tienda:
NSError:
Dominio = NSCocoaErrorDomain
Código = 256 "La operación no se pudo completar. (Error de cacao 256.)"
UserInfo = 0x1dd946a0 {NSUnderlyingException = autorización denegada, NSSQLiteErrorDomain = 23}
o
NSError:
Dominio = NSCocoaErrorDomain
Código = 256 "La operación no se pudo completar. (Error de cacao 256.)"
UserInfo = 0xc6525d0 {NSUnderlyingException = error de E / S de disco, NSSQLiteErrorDomain = 10}
Después de esta condición, la aplicación fallará b / c. Se requiere la tienda SQL para que la aplicación funcione. Podría intentar manejar esta falla de manera elegante probando un nuevo storeURL pero no quiero que el usuario pierda datos existentes. Además, nunca he reproducido personalmente este problema y, en función del bajo número de usuarios afectados y los registros de bloqueo, creo que es un problema de bajo impacto y no se repite en el siguiente lanzamiento de la aplicación.
Espero que exista un gurú de Core Data con algunas sugerencias sobre cómo depurar y prevenir / manejar estas condiciones. Mi código de inicialización de la pila de datos básicos proviene directamente del generador de proyectos xcode y he descartado cualquier problema de concurrencia en el sentido de que el coordinador de tiendas persistentes solo se inicializa una vez (en el inicio) y este error ocurre en esta inicialización.
Feliz de proporcionar más código / información si es relevante.
¡Gracias!
Parece que XJones y yo hemos podido localizar la causa de esto. Parece ser un error de borde de iOS o un comportamiento no documentado. He archivado esto en Apple bug ID 12935031.
Existe un escenario no contabilizado donde una aplicación que utiliza un cambio de ubicación importante o una supervisión regional no puede iniciarse correctamente (o tiene otras consecuencias imprevistas) debido a que a partir de iOS 5, las tiendas Core Data usan protección de datos (cifrado) defecto.
Pasos para reproducir:
1) Active la protección de código de acceso en el dispositivo
2) Cree una aplicación que inicie la supervisión de ubicación significativa o la supervisión de la región y la mantenga en funcionamiento incluso cuando esté en segundo plano. Es decir. Una aplicación que usa el cambio de ubicación significativo de fondo o la supervisión de la región.
3) Espere a que se agote la batería del dispositivo (puede haber otras causas también)
4) El dispositivo se apagará
5) Conecte el dispositivo a la Mac
6) Una vez que la carga es adecuada, el dispositivo se iniciará. Importante: no desbloquee el dispositivo en este momento.
7) Salga o ingrese al rango monitoreado o provoque un cambio significativo en la ubicación. El dispositivo ahora relanzará automáticamente la aplicación, ya que se registró para monitoreo de ubicación significativa o monitoreo de región
8) Sin embargo, dado que el dispositivo no ha sido desbloqueado por el usuario (aún no se ha ingresado el código de acceso), la aplicación no podrá leer ninguno de sus archivos de datos protegidos. En una aplicación Core Data, esto hará que el coordinador de tienda persistente no agregue el archivo de tienda persistente al contexto del objeto administrado. Esto hará que la aplicación se cuelgue o que, dependiendo del código utilizado por el desarrollador, intente restablecer la base de datos. En otras aplicaciones, puede causar bloqueos por otros motivos, ya que se trata de un efecto secundario inesperado e indocumentado de la función de protección de datos activada de manera predeterminada para las tiendas de datos centrales en iOS 5 y versiones posteriores.
La solución o solución alternativa hasta que Apple corrija esto o al menos documente es asegurarse de que su aplicación deje de supervisar los cambios / regiones significativos en la aplicaciónWillTerminate o desactivar la característica de protección de datos predeterminada configurando NSFileProtectionNone para la clave NSFileProtectionKey en el diccionario de opciones cuando agregando el almacén de datos centrales al coordinador de tienda persistente. esperar a que el almacén de archivos esté disponible usando un ciclo while () que verifica que los datos protegidos estén disponibles. Puede haber otras formas de hacerlo con KVO, pero este método funciona de manera confiable y es más fácil de insertar en el código existente sin necesidad de volver a procesar todo el proceso de inicio de la aplicación.
Actualización: parece que configurar esa clave no es suficiente si la protección de datos ya está activa en la tienda. Tienes que configurarlo manualmente:
[[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey] ofItemAtPath:storePath error:nil];
Aquí está la solución para las aplicaciones que necesitan monitoreo de ubicación de fondo, gracias a más aportes de XJones y un poco más de investigación en los documentos dispersos de Apple:
while(![[UIApplication sharedApplication] isProtectedDataAvailable]) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5f]];
}
Este código entra antes de intentar agregar el archivo de datos de la tienda al coordinador de tienda persistente.
Actualización 2015: también puede establecer NSPersistentStoreFileProtectionKey en NSFileProtectionNone. Esto desactivará correctamente la protección de archivos (si no la necesita) y solo funcionará sin requerir ninguna solución alternativa. La razón por la que no funcionó en intentos anteriores es porque la clave del diccionario con la que estaba realizando la prueba era incorrecta.
El mismo problema aquí, todavía no he podido encontrar una solución. Descubrí que parece estar relacionado con tener un código de bloqueo configurado en el dispositivo. Sin el código de bloqueo, nunca podría reproducir el error, ahora he podido hacerlo un montón de veces. El registro de la consola es:
El error es: Dominio de error = NSCocoaErrorDomain Code = 256 "La operación no se pudo completar. (Error de cacao 256.)" UserInfo = 0x1fd80110 {NSUnderlyingException = autorización denegada, NSSQLiteErrorDomain = 23}
El archivo existe y no utiliza ningún cifrado.
Acabo de notar las siguientes notificaciones en los documentos de UIApplication
:
UIApplicationProtectedDataDidBecomeAvailable
UIApplicationProtectedDataWillBecomeUnavailable
Estos están disponibles en iOS 4.0 y posterior. @lupinglade, creo que debe observar estas notificaciones y solo acceder a sus archivos protegidos (es decir, la tienda) después de recibir UIApplicationProtectedDataDidBecomeAvailable
.
La solución @lupinglade funcionó bastante bien en mi caso (la aplicación VoIP tenía al iniciar desde un reinicio), pero también me gustaría señalar que hay una función de delegado para AppDelegate a la que se llama cuando los datos protegidos están disponibles:
applicationProtectedDataDidBecomeAvailable:
Esto hizo que fuera un poco más fácil implementar la solución en mi caso.
De acuerdo con el archivo de encabezado, está disponible desde iOS 4 en adelante.