круглые поездки Типы данных Swift в / из данных

С Swift 3, склоняющимся к Data вместо [UInt8] , я пытаюсь выяснить, какой самый эффективный / идиоматический способ кодирования / декодирования swifts различных типов (UInt8, Double, Float, Int64 и т. Д.) В качестве объектов данных.

Вот этот ответ для использования [UInt8] , но, похоже, он использует различные API-интерфейсы указателей, которые я не могу найти в Data.

Я бы хотел, в основном, некоторые пользовательские расширения, которые выглядят примерно так:

 let input = 42.13 // implicit Double let bytes = input.data let roundtrip = bytes.to(Double) // --> 42.13 

Часть, которая действительно ускользает от меня, я просмотрел кучу документов, так это то, как я могу получить какую-то вещь-указатель (OpaquePointer или BufferPointer или UnsafePointer?) Из любой базовой структуры (которой все номера). В С, я бы просто ударил амперсанда перед ним, и я пойду.

Как создать Data из значения

struct Data имеет инициализатор

 public init(bytes: UnsafeRawPointer, count: Int) 

который можно использовать так же, как в различных ответах на вопрос. Как преобразовать double в массив байтов в swift? с которыми вы связаны:

 let input = 42.13 var value = input let data = withUnsafePointer(to: &value) { Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: input)) } print(data as NSData) // <713d0ad7 a3104540> 

Как уже сказал @zneak, вы можете взять адрес только переменной , поэтому переменная копия создается с помощью var value = value . В более ранних версиях Swift вы могли бы достичь этого, сделав параметр функции самой переменной, это больше не поддерживается.

Однако проще использовать инициализатор

 public init(buffer: UnsafeBufferPointer) 

вместо:

 let input = 42.13 var value = input let data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1)) print(data as NSData) // <713d0ad7 a3104540> 

Обратите внимание, что общий SourceType заполнителя автоматически выводится из контекста.

Как получить значение из Data

NSData было свойство bytes чтобы получить доступ к базовому хранилищу. struct Data имеет общий

 public func withUnsafeBytes(_ body: @noescape (UnsafePointer) throws -> ResultType) rethrows -> ResultType 

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

 let data = Data(bytes: [0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40]) let value = data.withUnsafeBytes { (ptr: UnsafePointer) -> Double in return ptr.pointee } print(value) // 42.13 

Если ContentType может быть выведен из контекста, то в закрытии его не нужно указывать, поэтому это можно упростить до

 let data = Data(bytes: [0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40]) let value: Double = data.withUnsafeBytes { $0.pointee } print(value) // 42.13 

Общее решение №1

Вышеперечисленные преобразования теперь могут быть легко реализованы как общие методы struct Data :

 extension Data { init(from value: T) { var value = value self.init(buffer: UnsafeBufferPointer(start: &value, count: 1)) } func to(type: T.Type) -> T { return self.withUnsafeBytes { $0.pointee } } } 

Пример:

 let input = 42.13 // implicit Double let data = Data(from: input) print(data as NSData) // <713d0ad7 a3104540> let roundtrip = data.to(type: Double.self) print(roundtrip) // 42.13 

Аналогично, вы можете преобразовывать массивы в Data и обратно:

 extension Data { init(fromArray values: [T]) { var values = values self.init(buffer: UnsafeBufferPointer(start: &values, count: values.count)) } func toArray(type: T.Type) -> [T] { return self.withUnsafeBytes { [T](UnsafeBufferPointer(start: $0, count: self.count/MemoryLayout.stride)) } } } 

Пример:

 let input: [Int16] = [1, Int16.max, Int16.min] let data = Data(fromArray: input) print(data as NSData) // <0100ff7f 0080> let roundtrip = data.toArray(type: Int16.self) print(roundtrip) // [1, 32767, -32768] 

Общее решение №2

Вышеупомянутый подход имеет один недостаток: Как и в том, как преобразовать double в массив байтов в swift? , он фактически работает только с «простыми» типами, такими как целые числа и типы с плавающей точкой. «Сложные» типы, такие как Array и String имеют (скрытые) указатели на базовое хранилище и не могут быть переданы путем простого копирования самой структуры. Он также не будет работать с ссылочными типами, которые просто указывают на хранилище реальных объектов.

Поэтому разрешим эту проблему, можно

  • Определите протокол, который определяет методы преобразования в Data и обратно:

     protocol DataConvertible { init?(data: Data) var data: Data { get } } 
  • Внедрить преобразования как методы по умолчанию в расширение протокола:

     extension DataConvertible { init?(data: Data) { guard data.count == MemoryLayout.size else { return nil } self = data.withUnsafeBytes { $0.pointee } } var data: Data { var value = self return Data(buffer: UnsafeBufferPointer(start: &value, count: 1)) } } 

    Я выбрал здесь неудачный инициализатор, который проверяет, соответствует ли количество байтов размеру этого типа.

  • И, наконец, объявить соответствие всем типам, которые можно безопасно преобразовать в Data и обратно:

     extension Int : DataConvertible { } extension Float : DataConvertible { } extension Double : DataConvertible { } // add more types here ... 

Это делает преобразование еще более элегантным:

 let input = 42.13 let data = input.data print(data as NSData) // <713d0ad7 a3104540> if let roundtrip = Double(data: data) { print(roundtrip) // 42.13 } 

Преимущество второго подхода заключается в том, что вы не можете непреднамеренно совершать небезопасные преобразования. Недостатком является то, что вы должны явно указывать все «безопасные» типы.

Вы также можете реализовать протокол для других типов, для которых требуется нетривиальное преобразование, например:

 extension String: DataConvertible { init?(data: Data) { self.init(data: data, encoding: .utf8) } var data: Data { // Note: a conversion to UTF-8 cannot fail. return self.data(using: .utf8)! } } 

или реализовать методы преобразования в ваших собственных типах, чтобы делать все необходимое, чтобы сериализовать и десериализовать значение.

Вы можете получить небезопасный указатель на изменяемые объекты, используя withUnsafePointer :

 withUnsafePointer(&input) { /* $0 is your pointer */ } 

Я не знаю способа получить один для неизменяемых объектов, потому что оператор inout работает только с изменяемыми объектами.

Это продемонстрировано в ответе, с которым вы связались.

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

 extension UInt16 : DataConvertible { init?(data: Data) { guard data.count == MemoryLayout.size else { return nil } self = data.withUnsafeBytes { $0.pointee } } var data: Data { var value = CFSwapInt16HostToBig(self)//Acho que o padrao do IOS 'e LittleEndian, pois os bytes estavao ao contrario return Data(buffer: UnsafeBufferPointer(start: &value, count: 1)) } } 

Проблема связана с LittleEndian и BigEndian.

  • Получить наибольший UIViewController
  • Как преобразовать массив байтов в шестнадцатеричную строку в Swift
  • Совместные запросы NSURLSession с Alamofire
  • Как преобразовать данные в шестнадцатеричную строку в swift
  • структура против classа в быстром языке
  • Swift - Пользовательский сеттер на свойстве
  • UIAlertController - добавление пользовательских представлений в таблицу действий
  • Как управлять расстоянием между строками в UILabel
  • Разница между == и ===
  • Значение типа «StorageMetadata» не имеет имени пользователя downloadURL '
  • Обновление задержки NavigationBar barTintColor iOS10
  • Interesting Posts

    Безопасные символы для дружественного URL-адреса

    Использование таблицы или скрипта в sed для замены многих специальных символов escape-символами?

    Стирание стилей стилей Java: когда и что происходит?

    Альтернативные, переплетенные или чередующиеся два вектора

    Как написать ключевой прослушиватель для отслеживания всех нажатий клавиш на Java?

    Советы по использованию Vim как Java IDE?

    Ошибка Grub "no such device" не соответствует конфигурации

    OSX: назначить расширение для контента

    Нажмите ViewController справа налево с помощью UINavigationController

    iOS6 MKMapView с использованием тонны памяти, вплоть до сбоя приложения, кто-нибудь еще заметил это?

    Ошибка при установке Ant: ANT_HOME установлен неправильно

    EF4 Code First: как добавить отношения без добавления свойства навигации

    Сгенерирование перестановок лениво

    Какое регулярное выражение никогда не будет соответствовать?

    ASP.NET MVC и Ajax, одновременные запросы?

    Давайте будем гением компьютера.