往返Swift编号类型到/从数据
Swift 3倾向于往返Swift编号类型到/从数据,swift,swift3,swift-data,Swift,Swift3,Swift Data,Swift 3倾向于数据而不是[UInt8],因此我试图找出将各种数字类型(UInt8、Double、Float、Int64等)作为数据对象进行编码/解码的最有效/惯用方法 有,但它似乎使用了各种指针API,我在数据上找不到 我想介绍一些定制扩展,它们看起来像: let input = 42.13 // implicit Double let bytes = input.data let roundtrip = bytes.to(Double) // --> 42.13 我已经浏览了很多
数据
而不是[UInt8]
,因此我试图找出将各种数字类型(UInt8、Double、Float、Int64等)作为数据对象进行编码/解码的最有效/惯用方法
有,但它似乎使用了各种指针API,我在数据上找不到
我想介绍一些定制扩展,它们看起来像:
let input = 42.13 // implicit Double
let bytes = input.data
let roundtrip = bytes.to(Double) // --> 42.13
我已经浏览了很多文档,但真正让我困惑的是如何从任何基本结构(所有数字都是)中获得某种指针(OpaquePointer、BufferPointer或UnsafePointer?)。在C语言中,我只需在它前面打一个符号,就可以了。您可以使用以下命令获得指向可变对象的不安全指针: 我不知道如何为不可变对象获取一个,因为inout操作符只适用于可变对象
这在您链接到的答案中得到了证明。您可以使用以下方法获得指向可变对象的不安全指针: 我不知道如何为不可变对象获取一个,因为inout操作符只适用于可变对象 这在您链接到的答案中得到了演示。注意:现在已为Swift 5(Xcode 10.2)更新了代码。(可以在编辑历史记录中找到Swift 3和Swift 4.2版本。)现在还可以正确处理未对齐的数据 如何从值创建
数据
从Swift 4.2开始,只需使用
let value = 42.13
let data = withUnsafeBytes(of: value) { Data($0) }
print(data as NSData) // <713d0ad7 a3104540>
这种方法有一个问题:它要求内存与类型的属性对齐(此处:与8字节地址对齐)。但这并不能保证,例如,如果数据是作为另一个数据
值的切片获得的
因此,将字节复制到值更安全:
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
var value = 0.0
let bytesCopied = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(bytesCopied == MemoryLayout.size(ofValue: value))
print(value) // 42.13
说明:
- 使用覆盖值的原始字节的可变缓冲区指针调用闭包
(数据符合该方法)将字节从数据复制到该缓冲区李>DataProtocol
copyBytes()
的返回值是复制的字节数。它等于目标缓冲区的大小,如果数据没有包含足够的字节,则等于或小于目标缓冲区的大小
通用解决方案#1
上述转换现在可以很容易地实现为结构数据的通用方法
:
extension Data {
init<T>(from value: T) {
self = Swift.withUnsafeBytes(of: value) { Data($0) }
}
func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
var value: T = 0
guard count >= MemoryLayout.size(ofValue: value) else { return nil }
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
return value
}
}
类似地,您可以将数组转换为数据
并返回:
extension Data {
init<T>(fromArray values: [T]) {
self = values.withUnsafeBytes { Data($0) }
}
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
protocol DataConvertible {
init?(data: Data)
var data: Data { get }
}
extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }
// add more types here ...
extension DataConvertible where Self: ExpressibleByIntegerLiteral{
init?(data: Data) {
var value: Self = 0
guard data.count == MemoryLayout.size(ofValue: value) else { return nil }
_ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}
var data: Data {
return withUnsafeBytes(of: self) { Data($0) }
}
}
我在这里选择了一个失败的初始值设定项,它检查提供的字节数
匹配类型的大小数据
并返回:
extension Data {
init<T>(fromArray values: [T]) {
self = values.withUnsafeBytes { Data($0) }
}
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
protocol DataConvertible {
init?(data: Data)
var data: Data { get }
}
extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }
// add more types here ...
let value = 42.13
let data = value.data
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = Double(data: data) {
print(roundtrip) // 42.13
}
或者在您自己的类型中实现转换方法来执行任何需要的操作
因此需要序列化和反序列化一个值
字节顺序
上述方法不进行字节顺序转换,数据始终为
主机字节顺序。对于独立于平台的表示(例如。
“big-endian”又名“network”字节顺序),使用相应的整数
财产。初始化者。例如:
let value = 1000
let data = value.bigEndian.data
print(data as NSData) // <00000000 000003e8>
if let roundtrip = Int(data: data) {
print(Int(bigEndian: roundtrip)) // 1000
}
let值=1000
让数据=value.bigEndian.data
打印(数据作为NSData)//
如果让往返=Int(数据:数据){
打印(Int(bigEndian:roundtrip))//1000
}
当然,这种转换通常也可以在泛型中完成
转换方法。注:Swift 5(Xcode 10.2)的代码现已更新。(可以在编辑历史记录中找到Swift 3和Swift 4.2版本。)现在还可以正确处理未对齐的数据
如何从值创建数据
从Swift 4.2开始,只需使用
let value = 42.13
let data = withUnsafeBytes(of: value) { Data($0) }
print(data as NSData) // <713d0ad7 a3104540>
这种方法有一个问题:它要求内存与类型的属性对齐(此处:与8字节地址对齐)。但这并不能保证,例如,如果数据是作为另一个数据
值的切片获得的
因此,将字节复制到值更安全:
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
var value = 0.0
let bytesCopied = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(bytesCopied == MemoryLayout.size(ofValue: value))
print(value) // 42.13
说明:
- 使用覆盖值的原始字节的可变缓冲区指针调用闭包
(数据符合该方法)将字节从数据复制到该缓冲区李>DataProtocol
copyBytes()
的返回值是复制的字节数。它等于目标缓冲区的大小,如果数据没有包含足够的字节,则等于或小于目标缓冲区的大小
通用解决方案#1
上述转换现在可以很容易地实现为结构数据的通用方法
:
extension Data {
init<T>(from value: T) {
self = Swift.withUnsafeBytes(of: value) { Data($0) }
}
func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
var value: T = 0
guard count >= MemoryLayout.size(ofValue: value) else { return nil }
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
return value
}
}
类似地,您可以将数组转换为数据
并返回:
extension Data {
init<T>(fromArray values: [T]) {
self = values.withUnsafeBytes { Data($0) }
}
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
protocol DataConvertible {
init?(data: Data)
var data: Data { get }
}
extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }
// add more types here ...
extension DataConvertible where Self: ExpressibleByIntegerLiteral{
init?(data: Data) {
var value: Self = 0
guard data.count == MemoryLayout.size(ofValue: value) else { return nil }
_ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}
var data: Data {
return withUnsafeBytes(of: self) { Data($0) }
}
}
我在这里选择了一个失败的初始值设定项,它检查提供的字节数
匹配类型的大小数据
并返回:
extension Data {
init<T>(fromArray values: [T]) {
self = values.withUnsafeBytes { Data($0) }
}
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
protocol DataConvertible {
init?(data: Data)
var data: Data { get }
}
extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }
// add more types here ...
let value = 42.13
let data = value.data
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = Double(data: data) {
print(roundtrip) // 42.13
}
或者在您自己的类型中实现转换方法来执行任何需要的操作
因此需要序列化和反序列化一个值
字节顺序
上述方法不进行字节顺序转换,数据始终为
主机字节顺序。对于独立于平台的表示(例如。
“big-endian”又名“network”字节顺序),使用相应的整数
财产。初始化者。例如:
let value = 1000
let data = value.bigEndian.data
print(data as NSData) // <00000000 000003e8>
if let roundtrip = Int(data: data) {
print(Int(bigEndian: roundtrip)) // 1000
}
let值=1000
让数据=value.bigEndian.data
打印(数据作为NSData)//
如果让往返=Int(数据:数据){
打印(Int(bigEndian:roundtrip))//1000
}
当然,这种转换通常也可以在泛型中完成
对流