栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

往返于数据的Swift数字类型

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

往返于数据的Swift数字类型

注意: 该代码现已针对 Swift 5 (Xpre 10.2)更新。(可以在编辑历史记录中找到Swift 3和Swift4.2版本。)现在,可能还可以正确处理未对齐的数据。

如何
Data
从价值中创造

从Swift 4.2开始,可以使用

let value = 42.13let data = withUnsafeBytes(of: value) { Data($0) }print(data as NSData) // <713d0ad7 a3104540>

说明:

  • withUnsafeBytes(of: value)
    使用覆盖该值原始字节的缓冲区指针来调用闭包。
  • 原始缓冲区指针是字节序列,因此
    Data($0)
    可以用来创建数据。

如何从中检索值
Data

从Swift
5开始,

withUnsafeBytes(_:)
of会
Data
使用
UnsafeMutableRawBufferPointer
对字节的“未类型化”
来调用闭包。该
load(fromByteOffset:as:)
方法从内存中读取值:

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

这种方法有一个问题:它要求内存针对该类型进行属性 对齐 (此处:对齐8字节地址)。但这不能保证,例如,如果数据是作为另一个

Data
值的切片获得的。

因此, 字节 复制 到以下值更安全:

let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])var value = 0.0let bytesCopied = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )assert(bytesCopied == MemoryLayout.size(ofValue: value))print(value) // 42.13

说明:

  • withUnsafeMutableBytes(of:_:)
    使用一个覆盖值原始字节的可变缓冲区指针来调用闭包。
  • 所述
    copyBytes(to:)
    的方法
    DataProtocol
    (到
    Data
    符合)复制从数据到该缓冲器中的字节。

返回值

copyBytes()
是复制的字节数。它等于目标缓冲区的大小,如果数据不包含足够的字节,则等于或小于目标缓冲区的大小。

通用解决方案#1

上面的转换现在可以很容易地实现为以下方法的通用方法

struct Data

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

在此

T: ExpressibleByIntegerLiteral
添加了约束,以便我们可以轻松地将值初始化为“零”
–这并不是真正的限制,因为无论如何该方法都可以与“特制”(整数和浮点)类型一起使用,请参见下文。

例:

let value = 42.13 // implicit Doublelet data = Data(from: value)print(data as NSData) // <713d0ad7 a3104540>if let roundtrip = data.to(type: Double.self) {    print(roundtrip) // 42.13} else {    print("not enough data")}

同样,您可以在 数组 之间

Data
来回转换:

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

例:

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

通用解决方案2

上面的方法有一个缺点:它实际上仅适用于“平凡”类型,例如整数和浮点类型。“复杂”类型喜欢

Array

String
具有指向基础存储的(隐藏)指针,并且不能仅通过复制结构本身来传递。它也不适用于引用类型,引用类型只是指向实际对象存储的指针。

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

  • 定义一个协议,该协议定义了

    Data
    往返转换的方法:

    protocol DataConvertible {init?(data: Data)var data: Data { get }

    }

  • 在协议扩展中将转换实现为默认方法:

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

    }

我在这里选择了一个 失败的 初始化程序,该初始化程序检查提供的字节数是否与类型的大小匹配。

  • 最后声明对所有可以安全转换的类型的一致性
    Data
    extension Int : DataConvertible { }

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

这使转换更加优雅:

let value = 42.13let data = value.dataprint(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 Data(self.utf8)    }}

或以自己的类型实现转换方法以执行所需的任何操作,以便对值进行序列化和反序列化。

字节顺序

在上述方法中,没有字节顺序转换,数据始终以主机字节顺序进行。对于平台无关的表示形式(例如“大端”或“网络”字节顺序),请使用相应的整数属性。初始化程序。例如:

let value = 1000let data = value.bigEndian.dataprint(data as NSData) // <00000000 000003e8>if let roundtrip = Int(data: data) {    print(Int(bigEndian: roundtrip)) // 1000}

当然,这种转换通常也可以用通用转换方法来完成。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/413193.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号