加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

往返Swift数字类型到/从数据

发布时间:2020-12-14 06:01:02 所属栏目:百科 来源:网络整理
导读:Swift 3倾向于Data而不是[UInt8],我试图找出最有效/惯用的编码/解码方式,将各种数字类型(UInt8,Double,Float,Int64等)转换为Data对象。 有this answer for using [UInt8],但似乎使用各种指针API,我不能在数据上找到。 我想基本上一些自定义扩展看起来
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)

其可以以与对问题的各种答案类似的方式使用
How to convert a double into a byte array in 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<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属性来访问底层存储。
struct数据有一个通用

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

上述方法有一个缺点:
How to convert a double into a byte array in swift?,
它实际上只与“简单”
类型如整数和浮点类型。 “复杂”类型如Array
和String有(隐藏)指向底层存储的指针,不能
通过只复制结构本身传递。它也不会工作
引用类型只是指向真实对象存储的指针。

所以解决这个问题,一个可以

>定义一个协议,定义转换为数据并返回的方法:

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)!
    }
}

或者在你自己的类型中实现转换方法来做任何事情必须这样序列化和反序列化一个值。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读