ios - ts1275 - the iphone could not be restored. an unknown error occurred(-1)
Problema especĂfico de iOS 12: Core Data External Storage Binary Data corrupt (1)
Actualización: el problema subyacente de los Datos básicos parece resolverse en iOS 12.1 (verificado en beta 4 ). Mantendremos la solución que se describe a continuación en nuestra aplicación y no recomendaremos el uso de la opción de almacenamiento externo en el futuro próximo.
Después de hablar con los ingenieros de Apple y archivar el Radar mencionado anteriormente , no pudimos esperar una solución, así que tomamos el golpe y cambiamos a almacenar archivos en el sistema de archivos y administrarlos directamente a nosotros mismos.
Otra alternativa que consideramos fue migrar nuestro modelo para no permitir almacenamiento externo para BLOB, pero no sé qué impacto habría tenido en el rendimiento y también estaba preocupado por la migración de un modelo en el momento en que parece que esta parte de iOS Sea inestable, especialmente después de leer historias como esta en el pasado: Datos básicos: no almacene archivos grandes como datos binarios - Alexander Edge - Medio
No fue demasiado doloroso implementar el almacenamiento local nosotros mismos. Solo necesita tener un identificador único para cada registro que pueda usar para crear un nombre de archivo para poder asignar archivos a registros. Agregamos una extensión a nuestra subclase de objetos gestionados con métodos para leer, escribir y eliminar archivos. Ahora, en lugar de llamar, por ejemplo, article.photo = image.pngData()
, ahora necesitamos llamar algo como article.savePhoto(image.pngData())
y luego hacemos algo similar cuando queremos recuperar la imagen. También puede agregar algo de código a estos métodos para admitir la compatibilidad con versiones anteriores que estén almacenadas actualmente en Core Data.
La eliminación fue un poco más complicada porque nuestros objetos se eliminan de múltiples lugares en el código, incluidas las eliminaciones en cascada. Al final, opté por hacerlo en el método prepareForDeletion
del objeto prepareForDeletion
, pero no es lo ideal. Hay mucha discusión sobre la mejor manera de implementar esto aquí: cacao: ¿Cómo manejar la limpieza de datos externos al eliminar objetos de Datos principales no guardados ? - Desbordamiento de pila
Finalmente, para evitar que nuestra aplicación falle cuando un atributo binario no Opcional ha desaparecido debido a este error, sobrescribo awakeFromFetch
en mi subclase de Objeto Administrado para asegurar que cualquier atributo requerido no sea nulo, y si lo son, los establezco en un marcador de posición Imagen para que se puedan guardar sin que la validación falle.
He pasado la mayor parte de un día de trabajo tratando de resolver esto.
Fondo
Tengo un modelo de datos básico simple, con libros y sesiones de lectura. Los libros tienen portadas (imágenes) que se almacenan como datos binarios con "Permite almacenamiento externo".
En iOS 11.4 y más abajo, todo funciona bien todo el tiempo. Cuando guardo una nueva sesión, todo se actualiza correctamente.
Problema
Desde iOS 12, cuando creo una nueva sesión de lectura y la vinculo al libro, casi cada segunda vez, los datos centrales generan una declaración SQL que también actualiza el campo de la cubierta del libro, lo que a veces resulta en una mala referencia (para archivar en el disco) que a menudo da como resultado que la cubierta sea nula al reiniciar la aplicación, y casi siempre crea una copia duplicada de la cubierta en el disco (como se puede ver en la carpeta _EXTERNAL_DATA
del _EXTERNAL_DATA
).
Sin embargo, el contexto y los objetos en memoria siguen siendo correctos (y, por lo tanto, todo en la interfaz de usuario está bien), hasta que la aplicación se reinicia, la cubierta suele ser nula .
iOS 12 específico
En iOS 12, puedo reproducir de forma determinista el error en el simulador, en dispositivos físicos, y los usuarios también han informado del error. No puedo reproducir el error en iOS 11.4, y ningún usuario informó el error anterior a iOS 12.
Pasos tomados
He habilitado "
-com.apple.CoreData.ConcurrencyDebug 1
", por lo que no debería ser que estoy accediendo a nada desde la cola incorrecta. También he habilitado "-com.apple.CoreData.SQLDebug 3
" para que pueda ver exactamente lo que se escribe.Me he asegurado de que la instancia de Libro (y, por lo tanto, la cubierta) no sea modificada por mi código antes de la asociación con la nueva sesión al verificar
hasChanges
, justo antes de que haganewSession.book = book
ycontext.save()
.Para estar 100% seguro de que no estoy tocando la propiedad de portada en ningún hilo he provocado un cortocircuito en mis captadores y configuradores para esa propiedad. Sin mejora.
He intentado usar
objectID
para solicitar una instancia del libro justo antes de la asociación y guardar. Sin mejora.Incluso he probado la opción en la que el contexto mantiene fuertes referencias a todos los objetos, solo para asegurarme de que no haya algún tipo de problema de administración de memoria. Sin mejora.
Pregunta
¿Alguna idea para los próximos pasos?
Actualización de estado
Esto es un defecto en iOS 12. Consulte la respuesta aceptada a continuación para obtener una descripción detallada de una solución razonable.