#ios #swift #xcode #encryption #core-data
Вопрос:
В приложении есть хранилище CoreData, где (помимо других данных) могут храниться изображения. Поскольку изображения могут быть разных размеров, эта Allows External Storage
настройка включена.
Иногда приложение рушится с ошибкой, указанной в названии:
Исключение NSInternalInconsistencyException Внешняя ссылка на данные не может найти базовый файл.
Это происходит только в выпущенном приложении, только для некоторой доли пользователей. Я никогда не мог воспроизвести его (установка Optimization level
в Fastest, Smallest [-Os]
значение также не помогла в воспроизведении).
Что видно из часового (используется для сообщения о сбоях):
- это происходит, когда приложение пытается извлечь объект из основных данных; объект обладает
data
свойством, которое является изображением, и уAllows External Storage
него включена настройка - это происходит, когда приложение находится в фоновом режиме (обычно более пары минут, но может произойти и быстрее).
- его нет в основном потоке.
Чтобы разрешить доступ к хранилищу из разных потоков, было реализовано следующее:
private var context: NSManagedObjectContext {
if Thread.current.isMainThread {
return persistentContainer.viewContext
}
return backgroundContext // this is initialised once with persistentContainer.newBackgroundContext()
}
Еще одна потенциально полезная информация заключается в том, что для основных данных включена защита:
// encrypt core data contents
// FileProtectionType.complete means that core data contents will be encrypted at all times when the device is locked
container.persistentStoreDescriptions.forEach {
$0.setOption(FileProtectionType.complete as NSObject, forKey: NSPersistentStoreFileProtectionKey)
}
Любая помощь в отношении идей о том, как исправить эту ошибку, будет более чем приветствоваться.
Заранее спасибо.
Комментарии:
1. Не совсем уверен, что понимаю, зачем вам нужна
context
реализация, которая возвращает либо контекст просмотра, либо фоновый контекст. Вы можете использовать контекст просмотра напрямую без диспетчерской работы сperform
учетом того, что вы находитесь в основном потоке, однако для фонового контекста вам всегда нужно использоватьperform
, поскольку невозможно сделать предположение о текущем контексте (очередь/поток). Также поддерживается поддержка постоянных контейнеровperformBackgroundTask
. Так что, возможно, виноват в том, как вы взаимодействуете с контекстами. У меня не так много кода для просмотра, так что просто подкидываю несколько идей.2. Мы не всегда хотим получать доступ к основным данным в основном потоке. Чтобы избежать проблем, мы создали специальную очередь. Внутри этой очереди используется выделенный контекст. Я уточнил эту деталь просто для того, чтобы дать понять, что это не просто проблема, не связанная с основным потоком (если, конечно, я не упускаю что-то еще).
3. Это не то, как параллелизм работает с основными данными. Если вы используете выделенную очередь, а не
perform
илиperformAndWait
(илиperformBackgroundTask
), то вы настраиваете себя на проблемы параллелизма. Core Data не заботится о вашей выделенной очереди, только о своих собственных очередях.4. Это то, что я обязательно рассмотрю и исследую. На самом деле, спасибо за этот комментарий. Тем не менее, я не думаю, что это причина этой конкретной проблемы, потому что а) это всегда происходит в фоновом режиме б) это происходит только с изображениями. Я предполагаю, что проблемы параллелизма не будут зависеть от режима приложения или типа данных?
5. Выдержка из документации:
Core Data uses thread (or serialized queue) confinement to protect managed objects and managed object contexts (see Core Data Programming Guide). A consequence of this is that a context assumes the default owner is the thread or queue that creates it. Don’t, therefore, initialize a context on one thread then pass it to another.
В любом случае я бы предложил использоватьperform
илиperformAndWait
. Я вижу, как проблемы с параллелизмом могут привести к несогласованному состоянию, особенно когда дело доходит до хранения файлов на диске.