Сохранить структуру для основных данных (НЕ КЛАСС) [дублировать]

Согласно спецификации POSIX для команды чтения , она должна вернуть ненулевой статус, если «обнаружен конец файла или произошла ошибка». Поскольку EOF обнаружен, когда он читает последнюю «строку», он устанавливает $ line, а затем возвращает статус ошибки, а статус ошибки предотвращает выполнение цикла на этой последней строке. Решение легко: сделайте цикл выполненным, если команда чтения выполнена успешно ИЛИ, если что-то было прочитано в строке $.

while read line || [ -n "$line" ]; do

29
задан Joshua Nozzi 9 June 2017 в 13:50
поделиться

3 ответа

Вы можете использовать интерфейс Codable с объектами CoreData для кодирования и декодирования данных, однако это не так автоматически, как при использовании с обычными старыми объектами Swift. Вот как можно реализовать JSON-декодирование непосредственно с объектами Core Data:

Во-первых, вы создаете свой объект Codable. Этот интерфейс должен быть определен на объекте, а не в расширении. Вы также можете определить свои кодирующие ключи в этом классе.

class MyManagedObject: NSManagedObject, Codable {
    @NSManaged var property: String?

    enum CodingKeys: String, CodingKey {
       case property = "json_key"
    }
}

Затем вы можете определить метод init. Это также должно быть определено в методе класса, потому что метод init требуется по протоколу Decodable.

required convenience init(from decoder: Decoder) throws {
}

Однако правильный инициализатор для использования с управляемыми объектами:

NSManagedObject.init(entity: NSEntityDescription, into context: NSManagedObjectContext)

Итак, секрет здесь заключается в использовании словаря userInfo для передачи в соответствующий контекстный объект в инициализатор. Для этого вам нужно расширить структуру CodingUserInfoKey с помощью нового ключа:

extension CodingUserInfoKey {
   static let context = CodingUserInfoKey(rawValue: "context")
}

Теперь вы можете так же, как декодер для контекста:

required convenience init(from decoder: Decoder) throws {

    guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError() }
    guard let entity = NSEntityDescription.entity(forEntityName: "MyManagedObject", in: context) else { fatalError() }

    self.init(entity: entity, in: context)

    let container = decoder.container(keyedBy: CodingKeys.self)
    self.property = container.decodeIfPresent(String.self, forKey: .property)
}

Теперь, когда вы настроите декодирование для управляемых объектов, вам нужно пройти по соответствующему объекту контекста:

let data = //raw json data in Data object
let context = persistentContainer.newBackgroundContext()
let decoder = JSONDecoder()
decoder.userInfo[.context] = context

_ = try decoder.decode(MyManagedObject.self, from: data) //we'll get the value from another context using a fetch request later...

try context.save() //make sure to save your data once decoding is complete

Чтобы кодировать данные, вам нужно сделать что-то подобное, используя кодировать функцию протокола.

44
ответ дан Zev Eisenberg 16 August 2018 в 03:55
поделиться
  • 1
    Я думаю, что UserInfoCodingKey правильно CodingUserInfoKey. – gonsee 27 October 2017 в 08:17
  • 2
    Отличная идея. Есть ли способ инициализировать и затем обновить существующие объекты таким образом? Например, проверьте, находится ли идентификатор в CoreData. Если он существует, загрузите объект и обновите, в противном случае создайте новый (как описано выше). – 1b0t 18 December 2017 в 22:51
  • 3
    Я бы добавил, что, поскольку значения декодирования из JSON могут быть выбрасывать, этот код потенциально позволяет объектам вставляться в контекст, даже если JSON-декодирование обнаружило ошибку. Вы можете поймать это из вызывающего кода и обработать его, удалив объект только что вставленного, но бросающего. – Tom Harrington 15 January 2018 в 17:51
  • 4
    Эй, я выполнил те же шаги, что и выше, но context.hasChanges всегда дает мне false, даже если у управляемого объекта есть значения после декодирования. Поскольку никаких изменений в контексте нет. Save не сохраняет. Я попытался вызвать context.save напрямую, я пропускаю без ошибок, но база данных пуста. Я также проверил указатель контекста, который был передан декодеру, и он тот же. Есть идеи? – Tarang 6 February 2018 в 14:29
  • 5
    @Tarang Вам удалось заставить его работать? У меня такая же проблема, база данных пуста, контекст не сохраняется. – Akhu 21 May 2018 в 09:33

CoreData - это собственная система сохранения и, в соответствии с его тщательной документацией, вы должны использовать назначенные им инициализаторы и следовать определенному пути к созданию и хранению объектов с ней.

Вы все равно можете использовать Codable с этим ограничены, как вы можете использовать NSCoding.

Одним из способов является декодирование объекта (или структуры) с помощью любого из этих протоколов и перенос его свойств в новый экземпляр NSManagedObject, который вы создали для документов Core Data.

Другой способ (который очень распространен) - использовать один из протоколов только для нестандартного объекта, который вы хотите сохранить в свойствах управляемого объекта. Под «нестандартным» я имею в виду все, что не соответствует стандартным типам атрибутов Core Data, как указано в вашей модели. Например, NSColor не может быть сохранен непосредственно как свойство управляемого объекта, поскольку он не является одним из базовых типов CD-носителей. Вместо этого вы можете использовать NSKeyedArchiver для сериализации цвета в экземпляре NSData и сохранить его как свойство Data в управляемом объекте. Переверните этот процесс с помощью NSKeyedUnarchiver. Это упрощенно, и с Core Data намного лучший способ сделать это (см. Атрибуты переходных процессов ), но это иллюстрирует мою точку зрения.

Вы также могли бы принять Encodable (один из двух протоколов, которые составляют Codable - можете ли вы угадать имя другого?), чтобы преобразовать экземпляр управляемого объекта непосредственно в JSON для совместного использования, но вы 'd иметь указать ключи кодирования и собственную собственную реализацию encode, поскольку она не будет автоматически синтезирована компилятором с помощью пользовательских ключей кодирования. В этом случае вы хотите указать только ключи (свойства), которые вы хотите включить.

Надеюсь, это поможет.

10
ответ дан Joshua Nozzi 16 August 2018 в 03:55
поделиться
  • 1
    Спасибо за подробное объяснение. В настоящее время я использую первый подход, как вы упомянули. Но я действительно надеюсь, что NSManagedObject может соответствовать Codable по умолчанию, и есть методы, такие как json = encoder.encode(foo) для его кодирования напрямую, и foo = decoder.decode(Foo.self, json, context) для декодирования напрямую. Надеюсь увидеть его в обновлении или в следующем крупном выпуске. – hgl 10 June 2017 в 02:21
  • 2
    Я бы на это не рассчитывал. Возможность настроить кодирование / декодирование в значительной степени охватывает все базы для передачи данных между магазином вашего приложения и большинством реальных случаев с помощью JSON de / coder. Поскольку оба подхода являются взаимоисключающими для сохранения приложений (из-за их радикально разных подходов к проектированию и вариантов использования), существует примерно нулевая вероятность такой поддержки. Но надежда вечна. ;-) – Joshua Nozzi 10 June 2017 в 04:35
  • 3
    @JoshuaNozzi Я полностью не согласен с этим комментарием. Вы можете легко изменить сопоставления, и люди используют библиотеки для этого конкретного подхода. Я бы не удивился, если бы поддержка пришла через 2 или около того итерации iOS в будущем. Это просто потребует настройки протокола для поддержки населения без инициализации или соответствия базового уровня текущим интерфейсам инициализации CoreData и генерации кода кодирования Codable для моделей CoreData (которые у них уже есть генерация кода). Подходы не являются взаимоисключающими, и 99% приложений, использующих основные данные, представляют собой сопоставление JSON. – TheCodingArt 15 August 2017 в 00:06
  • 4
    @TheCodingArt К чему вы относитесь? Типы пользовательских магазинов? Это немного отличается от прямого использования Codable / Decodable непосредственно на отдельных управляемых объектах, кроме машин Core Data. – Joshua Nozzi 15 August 2017 в 00:17
  • 5
    @JoshuaNozzi Я никогда не ссылался ни на какие типы пользовательских магазинов ... Это простое сопоставление сериализации / десериализации свойств в Swift с кодовыми значениями кода, генерируемыми кодом. – TheCodingArt 15 August 2017 в 00:18

Swift 4.2:

Следуя решению casademora,

guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError() }

должно быть

guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() }.

Это предотвращает ошибки, которые Xcode ложно распознает как проблемы среза массива.

3
ответ дан Schemetrical 16 August 2018 в 03:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: