Автоматическое кодирование и декодирование с наследованием в Swift [дубликат]

Строки в Java неизменяемы. Это означает, что всякий раз, когда вы пытаетесь изменить / изменить строку, вы получаете новый экземпляр. Вы не можете изменить исходную строку. Это сделано для того, чтобы эти экземпляры строк могли кэшироваться. Типичная программа содержит множество ссылок на строки и кеширование этих экземпляров, что может уменьшить объем памяти и увеличить производительность программы.

При использовании оператора == для сравнения строк вы не сравниваете содержимое строки , но фактически сравнивают адрес памяти. Если они равны, в противном случае они вернут true и false. Если значение равно в строке, сравнивает содержимое строки.

Итак, вопрос в том, что все строки кэшируются в системе, как получается == возвращает false, тогда как equals возвращает true? Ну, это возможно. Если вы создадите новую строку, например String str = new String("Testing"), вы создадите новую строку в кеше, даже если в кеше уже содержится строка с тем же содержимым. Короче говоря, "MyString" == new String("MyString") всегда будет возвращать false.

Java также говорит о функции intern (), которая может использоваться в строке, чтобы сделать ее частью кеша, поэтому "MyString" == new String("MyString").intern() вернет true.

Примечание: == оператор намного быстрее, чем равен только потому, что вы сравниваете два адреса памяти, но вы должны быть уверены, что код не создает новые экземпляры String в коде. В противном случае вы столкнетесь с ошибками.

39
задан Guilherme 18 October 2017 в 18:03
поделиться

4 ответа

Я считаю, что в случае наследования вы должны реализовать Coding самостоятельно. То есть вы должны указать CodingKeys и реализовать init(from:) и encode(to:) как в суперклассе, так и в подклассе. WWDC видео (около 49:28, на фото ниже), вы должны вызвать супер с суперкодером / декодером.

required init(from decoder: Decoder) throws {

  // Get our container for this subclass' coding keys
  let container = try decoder.container(keyedBy: CodingKeys.self)
  myVar = try container.decode(MyType.self, forKey: .myVar)
  // otherVar = ...

  // Get superDecoder for superclass and call super.init(from:) with it
  let superDecoder = try container.superDecoder()
  try super.init(from: superDecoder)

}

Видео, кажется, перестает показывать сторону кодирования (но это container.superEncoder() для стороны encode(to:)), но она работает примерно так же в вашей реализации encode(to:). Я могу подтвердить, что это работает в этом простом случае (см. Ниже код детской площадки).

Я все еще борюсь с каким-то нечетным поведением с гораздо более сложной моделью, которую я конвертирую из NSCoding, у которой много новых типов (включая struct и enum), это демонстрирует это неожиданное поведение nil и «не должно быть». Просто имейте в виду, что могут быть краевые случаи, связанные с вложенными типами.

Редактировать: вложенные типы, похоже, отлично работают на моей тестовой площадке; Теперь я подозреваю, что что-то не так с классами саморегуляции (подумайте о детях узлов дерева) с самим набором, который также содержит экземпляры этого класса под разными классами. Тест простого класса саморегуляции отлично декодирует (т. Е. Никаких подклассов), поэтому я теперь сосредоточиваю свои усилия на том, почему случай подкласса терпит неудачу.

Обновление 25 июня 17: Я закончил тем, что написал об этом Apple. rdar: // 32911973 - К сожалению, цикл кодирования / декодирования массива из Superclass, который содержит элементы Subclass: Superclass, приведет к тому, что все элементы в массиве будут декодированы как Superclass (подкласс «init(from:) никогда не вызывается, что приводит к потере данных или к худшему).

//: Fully-Implemented Inheritance

class FullSuper: Codable {

    var id: UUID?

    init() {}

    private enum CodingKeys: String, CodingKey { case id }

    required init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(UUID.self, forKey: .id)

    }

    func encode(to encoder: Encoder) throws {

        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)

    }

}

class FullSub: FullSuper {

    var string: String?
    private enum CodingKeys: String, CodingKey { case string }

    override init() { super.init() }

    required init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        let superdecoder = try container.superDecoder()
        try super.init(from: superdecoder)

        string = try container.decode(String.self, forKey: .string)

    }

    override func encode(to encoder: Encoder) throws {

        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(string, forKey: .string)

        let superdecoder = container.superEncoder()
        try super.encode(to: superdecoder)

    }
}

let fullSub = FullSub()
fullSub.id = UUID()
fullSub.string = "FullSub"

let fullEncoder = PropertyListEncoder()
let fullData = try fullEncoder.encode(fullSub)

let fullDecoder = PropertyListDecoder()
let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData)

Оба свойства супер- и подкласса восстанавливаются в fullSubDecoded.

38
ответ дан Joshua Nozzi 15 August 2018 в 17:23
поделиться
  • 1
    Рад, что я могу помириться с этой проблемой хотя бы на данный момент ... – Charlton Provatas 29 August 2017 в 00:50
  • 2
    на данный момент удалось обойти эту проблему, преобразовывая базовый класс в протокол и добавляя реализации по умолчанию к расширению протокола и поддерживая производный класс – Charlton Provatas 29 August 2017 в 01:03
  • 3
    Точно так же, как Чарльтон. Выполнял ошибки EXC_BAD_ACCESS при декодировании базовым классом. Пришлось перейти к структуре протокола, чтобы обойти его. – Harry Bloom 25 September 2017 в 11:56
  • 4
    На самом деле container.superDecoder() не требуется. super.init (из: декодер) достаточно – Lal Krishna 6 June 2018 в 09:39
  • 5
    Я запускаю код swift 4.1. И я получил исключение при использовании superDecoder. И работая отлично с super.init(from: decoder) – Lal Krishna 7 June 2018 в 04:55

Мне удалось заставить его работать, если мой базовый класс и подклассы соответствуют Decodable вместо Codable. Если бы я использовал Codable, он бы разбился нечетными способами, такими как получение EXC_BAD_ACCESS при доступе к полю подкласса, но отладчик мог без проблем отображать все значения подкласса.

Кроме того, передача супердекодера в базовый класс в super.init() не сработала. Я просто передал декодер из подкласса в базовый класс.

4
ответ дан Milad Faridnia 15 August 2018 в 17:23
поделиться
  • 1
    Тот же трюк: передача супердекодера в базовый класс в super.init () не сработала. Я просто передал декодер из подкласса в базовый класс. – Jack Song 30 December 2017 в 12:24
  • 2
    столкнулся с такой же проблемой. есть ли способ решить это без полного внедрения методов кодирования / декодирования? благодаря – Doro 28 May 2018 в 15:14

Как насчет использования следующего пути?

protocol Parent: Codable {
    var inheritedProp: Int? {get set}
}

struct Child: Parent {
    var inheritedProp: Int?
    var title: String?

    enum CodingKeys: String, CodingKey {
        case inheritedProp = "inherited_prop"
        case title = "short_title"
    }
}

Дополнительная информация о композиции: http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/

4
ответ дан Nav 15 August 2018 в 17:23
поделиться
  • 1
    Как это решает проблему декодирования гетерогенного массива? – Joshua Nozzi 19 October 2017 в 15:25
  • 2
    Для того, чтобы быть ясным, это была не злобная критика. Я продолжаю пересматривать проблему хранения гетерогенных коллекций безрезультатно. Общее решение является лучшим, а это значит, что мы не можем знать типы во время декодирования. – Joshua Nozzi 7 November 2017 в 17:55
  • 3
    В Xcode в разделе Help & gt; Документация разработчика, найдите отличную статью под названием «Кодирование и декодирование пользовательских типов». Я думаю, что чтение поможет вам. – Tommie C. 13 November 2017 в 14:30
  • 4
    Я пытаюсь сделать это, но я продолжаю получать ошибку времени выполнения при кодировании данных, хранящихся в массиве. «Неустранимая ошибка: Массив & lt; Родитель & gt; не соответствует кодируемому, поскольку Parent не соответствует кодированию. & quot; Любая помощь? – Natanel 9 January 2018 в 18:23
  • 5
    Это не композиция. – mxcl 16 May 2018 в 18:28

Найдено эта ссылка - перейти к разделу наследования

override func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(employeeID, forKey: .employeeID)
}

Для декодирования я сделал это:

 required init(from decoder: Decoder) throws {

    try super.init(from: decoder)

    let values = try decoder.container(keyedBy: CodingKeys.self)
    total = try values.decode(Int.self, forKey: .total)
  }

private enum CodingKeys: String, CodingKey
{
    case total

}
3
ответ дан user2704776 15 August 2018 в 17:23
поделиться
Другие вопросы по тегам:

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