swift: nscoding decodeObject с нулевым значением все время

#ios #swift #nscoding #multipeer-connectivity #nskeyedunarchiver

#iOS #swift #nscoding #multipeer-подключение #nskeyedunarchiver

Вопрос:

Я получил следующие коды при записи объекта с именем Packet и отправке на другую сторону через Multipeer-соединение. Однако я получал следующую ошибку всякий раз, когда он пытался декодировать закодированный объект.

   class Packet : NSObject, NSCoding {

  var tmp1: Double = 0
  var tmp2: Double = 0

  struct PropertyKey {
    static let tmp1Key = "tmp1Key"
    static let tmp2Key = "tmp2Key"
  }


  init(tmp1: Double, tmp2: Double) {
    self.tmp1 = tmp1
    self.tmp2 = tmp2
    super.init()
  }

  deinit {
  }

  required convenience init(coder aDecoder: NSCoder) {
    debugPrint("initcoder")
    let tmp1 = aDecoder.decodeObject(forKey: PropertyKey.tmp1Key) as! Double // crash here
    let tmp2 = aDecoder.decodeObject(forKey: PropertyKey.tmp2Key) as! Double
    self.init(tmp1: tmp1, tmp2: tmp2)
  }

  public func encode(with aCoder: NSCoder) {
    debugPrint("encodeCoder")
    aCoder.encode(tmp1, forKey: PropertyKey.tmp1Key)
    aCoder.encode(tmp2, forKey: PropertyKey.tmp2Key)
  }
}
  

Ошибка, которую я получил, такова
—- Распечатать от меня —-
неустранимая ошибка «initcoder»: неожиданно обнаружено значение nil при развертывании необязательного значения
2016-09-30 13:32:55.901189 Подключение [323:33022] неустранимая ошибка: неожиданно обнаружено значение nil при развертывании необязательного значения

Но когда я создаю объект, все значения устанавливаются. Я заключил контракт с объектом Packet без проблем.

—- Дополнительная информация —— Я использовал следующие коды для кодирования и декодирования данных при отправке на другую сторону через подключение множителя.

  func dataForPacket(packet: Packet) -> Data {
    let data = NSMutableData()
    let archiver = NSKeyedArchiver(forWritingWith: data)
    archiver.encode(packet, forKey: "Packet")
    archiver.finishEncoding()
    debugPrint("dataForPacket (data) (packet)")
    return data as Data
  }

  func packetForData(_ data: Data) -> Packet {
    debugPrint("packetForData")
    let unarchiver = NSKeyedUnarchiver(forReadingWith: data)

    let packet: Packet = unarchiver.decodeObject(forKey: "Packet") as! Packet 
     // crash here (as this will call the init of the Packet class)
    debugPrint("packetForData (packet)")
    return packet
  }
  

Интересно, что может вызвать ошибку. Спасибо.

Комментарии:

1. Вам нужно использовать decodeDouble для ключа

2. let tmp1 = aDecoder.decodeDouble(forKey: PropertyKey.tmp1Key)

3. Как упоминалось в приведенных выше комментариях, вы должны использовать определенное декодирование для целых чисел, чисел с плавающей запятой, двоичных чисел, двоичных чисел и т. Д..

4. Большое спасибо!! Это решило мою проблему!

5. Я обнаружил еще одну проблему: когда в моем пакете класса есть переменные класса других типов (например, массивы, UIViews), а затем я использовал decodeDouble для tmp1 и tmp2, но затем использовал decodeObject для остальных. Затем происходит сбой и говорится, что tmp1 и tmp2 не имеют типа Double (хотя на самом деле они имеют тип Double). и это больше не сбой, когда я меняю их на decodeObject. Почему это так?

Ответ №1:

Swift 3:

Я кодировал двойное значение. Используйте decoder.containsValue, чтобы проверить, было ли закодировано значение.

И используйте decodeDouble(forKey:). Не decodeObject (forKey 🙂

  var dateTime: TimeInterval = SOME_TIME_INTERVAL (Double)//Can be saved(encoded) previously, and not encoded as well.
 aCoder.encode(dateTime, forKey: "dateTime")
 if decoder.containsValue(forKey: "dateTime") {
            dateTime = decoder.decodeDouble(forKey: "dateTime")
        }
  

Комментарии:

1. Примечание: При декодировании строк используйте decodeObject(forKey:)

Ответ №2:

Swift 4.2:

При использовании decodeObject(forKey:) для декодирования примитивных типов он возвращает nil . Потому что перегрузка encode(_:forKey:) принимает Int напрямую и записывает его тип NSNumber, и вы не можете декодировать его как объект.

decodeInteger(forKey:) правильно считывает его как Int и возвращает его. Вместо этого вы можете использовать decodeInteger(forKey:) Int(decodeInt32(forKey:)) или Int(decodeInt64(forKey:)) .

Ответ №3:

При кодировании или декодировании примитивных типов, таких как Double и Bool, вы должны использовать указанные методы decodeDouble:forKey и decodeBool:forKey , соответственно, в противном случае они не смогут декодировать и возвращать nil.

Ответ №4:

В моем случае класс, который я пытался декодировать, был NSDictionary, и он продолжал возвращаться с нулевым значением после того, как я переработал свой код для использования не устаревших функций в NSKeyedUnarchiver в Swift 5.3.

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

 unarchiver.containsValue(forKey: saveKey)
  

Я нашел проблему. Если я использую новую не устаревшую функцию, она сработала:

РАБОЧИЙ код:

 let unarchiver = try NSKeyedUnarchiver(forReadingWith: data)  // forReadingWith now has a deprecated warning.. ?? but the alternative does not work.
var unarchived = unarchiver.decodeObject(forKey: saveKey) // nil
  

НЕРАБОТАЮЩИЙ код — не работает, но использует устаревшую функцию:

 let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
var unarchived = unarchiver.decodeObject(forKey: saveKey) // not nil - works
  

Комментарии:

1. Вы когда-нибудь находили решение, которое включало бы использование современных (не устаревших) API? Интересно, потому что мы хотим прочитать ранее сохраненные данные, и я все еще вижу nil . @TheJeff

2. Убедитесь, что вы также пишете с не устаревшей функцией

3. decodeTopLevelObject исправлена проблема для меня