往返Swift数字类型到/从数据
Swift 3倾向于Data而不是[UInt8],我试图找出最有效/惯用的编码/解码方式,将各种数字类型(UInt8,Double,Float,Int64等)转换为Data对象。
有this answer for using [UInt8],但似乎使用各种指针API,我不能在数据上找到。 我想基本上一些自定义扩展看起来像: let input = 42.13 // implicit Double let bytes = input.data let roundtrip = bytes.to(Double) // --> 42.13 这部分真的逃避了我,我看过一堆文档,是我怎么能从任何基本结构(所有的数字)得到一些指针事物(OpaquePointer或BufferPointer或UnsafePointer?)。在C,我只是在它前面拍了一个&符号,并有ya去。
如何从值创建数据
struct Data有一个初始化器 public init(bytes: UnsafeRawPointer,count: Int) 其可以以与对问题的各种答案类似的方式使用 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已经说过,你可以只取一个变量的地址, 但是,使用初始化程序更容易 public init<SourceType>(buffer: UnsafeBufferPointer<SourceType>) 代替: let input = 42.13 var value = input let data = Data(buffer: UnsafeBufferPointer(start: &value,count: 1)) print(data as NSData) // <713d0ad7 a3104540> 请注意,通用占位符SourceType将从中自动推断 如何从数据中检索值 NSData有一个bytes属性来访问底层存储。 public func withUnsafeBytes<ResultType,ContentType>(_ body: @noescape (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType 相反,它可以这样使用: let data = Data(bytes: [0x71,0x3d,0x0a,0xd7,0xa3,0x10,0x45,0x40]) let value = data.withUnsafeBytes { (ptr: UnsafePointer<Double>) -> Double in return ptr.pointee } print(value) // 42.13 如果ContentType可以从上下文推断,则不需要指定它 let data = Data(bytes: [0x71,0x40]) let value: Double = data.withUnsafeBytes { $0.pointee } print(value) // 42.13 通用解决方案#1 以上转换现在可以轻松实现为struct的通用方法Data: extension Data { init<T>(from value: T) { var value = value self.init(buffer: UnsafeBufferPointer(start: &value,count: 1)) } func to<T>(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 类似地,您可以将数组转换为数据并返回: extension Data { init<T>(fromArray values: [T]) { var values = values self.init(buffer: UnsafeBufferPointer(start: &values,count: values.count)) } func toArray<T>(type: T.Type) -> [T] { return self.withUnsafeBytes { [T](UnsafeBufferPointer(start: $0,count: self.count/MemoryLayout<T>.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 上述方法有一个缺点: 所以解决这个问题,一个可以 >定义一个协议,定义转换为数据并返回的方法: protocol DataConvertible { init?(data: Data) var data: Data { get } } >在协议扩展中将转换实现为默认方法: extension DataConvertible { init?(data: Data) { guard data.count == MemoryLayout<Self>.size else { return nil } self = data.withUnsafeBytes { $0.pointee } } var data: Data { var value = self return Data(buffer: UnsafeBufferPointer(start: &value,count: 1)) } } 我在这里选择一个可用的初始化程序,检查提供的字节数 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)! } } 或者在你自己的类型中实现转换方法来做任何事情必须这样序列化和反序列化一个值。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |