往返数据的 Swift 数字类型

2023-11-25

Swift 3 倾向于Data代替[UInt8],我试图找出将各种数字类型(UInt8、Double、Float、Int64 等)编码/解码为数据对象的最有效/惯用的方法。

There's 这个答案使用 [UInt8],但它似乎使用了我在 Data 上找不到的各种指针 API。

我基本上想要一些看起来像这样的自定义扩展:

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

真正让我困惑的部分是,我浏览了一堆文档,是如何从任何基本结构(所有数字都是)中获取某种指针的东西(OpaquePointer 或 BufferPointer 或 UnsafePointer?)。在 C 语言中,我只需在它前面加上一个 & 符号,然后就可以了。


Note:代码已更新为Swift 5(Xcode 10.2)现在。 (Swift 3 和 Swift 4.2 版本可以在编辑历史记录中找到。)现在也可以正确处理可能未对齐的数据。

如何创建Data从一个值

从 Swift 4.2 开始,可以简单地从值创建数据

let value = 42.13
let 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

这种方法有一个问题:它要求内存具有属性aligned类型(此处:与 8 字节地址对齐)。但这并不能保证,例如如果数据是作为另一个数据的切片获得的Data value.

因此更安全的是copy值的字节:

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

解释:

  • withUnsafeMutableBytes(of:_:)使用覆盖值的原始字节的可变缓冲区指针调用闭包。
  • The 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在这里添加,以便我们可以轻松地将值初始化为“零”——这实际上并不是一个限制,因为无论如何该方法都可以与“trival”(整数和浮点)类型一起使用,请参见下文。

Example:

let value = 42.13 // implicit Double
let 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")
}

同样,您可以转换arrays to 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
    }
}

Example:

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

    我选择了一个failable这里的初始化程序检查提供的字节数 与类型的大小相匹配。

  • 最后声明与所有可以安全转换的类型的一致性Data然后回来:

    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
}

第二种方法的优点是您不会无意中进行不安全的转换。缺点是您必须显式列出所有“安全”类型。

您还可以为需要重要转换的其他类型实现协议,例如:

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

或在您自己的类型中实现转换方法来执行任何操作 因此序列化和反序列化一个值是必要的。

字节顺序

上述方法中没有进行字节顺序转换,数据始终在 主机字节顺序。对于独立于平台的表示(例如 “big endian”又名“网络”字节顺序),使用相应的整数 属性分别。初始化器。例如:

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
}

当然这种转换也可以一般地完成,在通用的 转换方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

往返数据的 Swift 数字类型 的相关文章

  • 如何从 Swift 调用 Objective-C 代码?

    在 Swift 中 如何调用 Objective C 代码 Apple 提到它们可以在一个应用程序中共存 但这是否意味着在技术上可以重用 Objective C 中创建的旧类 同时在 Swift 中构建新类 在 Swift 中使用 Obje
  • 为什么 Obj-C 属性默认所有权“分配”而不是“强”

    我正在向旧项目添加 Swift 类 一切进展顺利 直到我尝试向 Swift 类添加属性 生成的标头无法编译 我认为问题是 在生成的代码中 Swift 省略了strong所有权并仅将其声明为nonatomic 这通常应该足够了 因为 prop
  • CLLocation Manager如何在一定距离后更新

    我正在使用 CLLocationManager didupdatelocations 如下所示 func locationManager manager CLLocationManager didUpdateLocations locati
  • tableView.dequeueReusableCellWithIdentifier() 导致应用程序挂起

    原帖 我们最近将我们的应用程序转换为 Swift 2 0 和 iOS9 我看到的一个奇怪的问题是调用 tableView dequeueReusableCellWithIdentifier 会导致应用程序挂在模拟器中 The code fu
  • 如何使 SFSpeechRecognizer 在 macOS 上可用?

    我正在尝试使用 Apple 的语音框架在 macOS 10 15 1 上进行语音识别 在 macOS 10 15 之前 语音识别仅在 iOS 上可用 但根据文档 https developer apple com documentation
  • SwiftUI 列表与右侧的部分索引?

    是否可以有一个在右侧有索引的列表 就像下面 SwiftUI 中的示例一样 我在 SwiftUI 中做了这个 Contacts swift TestCalendar Created by Christopher Riner on 9 11 2
  • UIButton 导致无法识别的选择器发送到实例

    我正在尝试使用 for 循环创建多个按钮 但在使用 sender 函数时遇到问题 我有以下代码 func setUpButtons for i in 1 3 let btn UIButton UIButton frame CGRect x
  • 确定 SceneKit 中 SKVideoNode 的视频大小/长宽比

    如何从 AVPlayer 获取视频的视频大小来设置节点的几何大小 例如 我有一个具有宽度和高度的 SCNPlane let planeGeo SCNPlane width 5 height 5 所以现在我实例化我的视频播放器 let vid
  • GeoFire Swift 3 - 保存和更新坐标

    我正在尝试使用 GeoFire 将坐标存储到 Firebase 数据库中 我不确定如何更新新坐标 因为它们每秒都会更改 更新 随着childByAutoId 它正在为每辆自行车生成一个新的唯一 ID 如何引用这个唯一的自行车 ID 例如 用
  • 如何使用 Swift 使用 TouchID?

    Apple 为 iOS 8 的 TouchID 实现提供的文档采用 Objective C 语言 有 Swift 版本吗 Objective C IBAction touchIDAvailable UIButton touchIDAvail
  • Swift:使具有相同“形状”的两种类型符合通用协议

    我有两种不同的类型 它们代表相同的数据 并且具有完全相同的 形状 这两种不同的类型是代码生成的 我被迫处理它们 但是 我想让它们符合一个通用的协议 这样我就可以对这两种类型一视同仁 这是一个例子 假设这是我所坚持的两种代码生成类型 stru
  • 如何让按钮闪烁?

    我试图在扫描正确时将按钮的颜色 只是闪烁 闪烁 更改为绿色 在出现问题时将按钮的颜色更改为红色 我可以用这样的视图来做到这一点 func flashBG UIView animateWithDuration 0 7 animations s
  • SwiftUI:发送电子邮件

    在正常情况下UIViewController在 Swift 中 我使用此代码发送邮件 let mailComposeViewController configuredMailComposeViewController mailCompose
  • Swift:设置协议的可选属性

    如何设置协议的可选属性 例如 UITextInputTraits 有许多可选的读 写属性 当我尝试以下操作时 出现编译错误 无法分配给 textInputTraits 中的 keyboardType func initializeTextI
  • 在 WKWebView 中禁用缩放?

    有谁知道在 WKWebView 中禁用双击和捏缩放的简单方法 我尝试过的任何方法都不起作用 Webview scrollView allowsMagnification false Error value of type WKWebView
  • 根据内容自动更改单元格高度 - Swift

    在 Swift 中使用 UITableView 有人可以帮我根据标签 图片和描述自动更改单元格的高度吗 所有信息都正确传递 我只需要帮助格式化它 我尝试使用调整它cell frame size height 但这没有效果 我可以更改故事板中
  • 如何使用 IOS 12 在 UITableViewCell 中正确添加 UICollectionView

    由于某些原因 在使用 Xcode 10 beta 时 我无法正确显示 tableview 单元格内集合中的某些项目 在过去的四天里我尝试了我所知道的一切 我做了一个小项目样本来看看我的问题是什么 如果有人想在本地运行完整代码 请参见此处 h
  • 通过 Button Swift 中的标签发送行和部分

    我里面有这个cellForRowAtIndexPath cell plusBut tag indexPath row cell plusBut addTarget self action plusHit forControlEvents U
  • 在 SwiftUI 中使用可观察对象切换视图

    我正在练习尝试使用 SwiftUI 中的可观察对象切换视图 但我的代码无法正常工作 我知道我可以用 State 来做到这一点 但我想用可观察的对象来实现这一点 当我单击内容视图中的图像时 图像不会改变 有人能帮我吗 内容视图 swift i
  • 在 Object 子类及其自己的子类上实现ignoreProperties()

    我是领域新手 我正在使用继承自 Object 的基类以及该基类的自定义子类创建模型 我的模型要求基类通过覆盖静态来声明一些属性被忽略ignoredProperties 方法 当尝试在某些基类子类上重写该方法时 我收到一个 Swift 编译器

随机推荐

  • 使用可变参数模板显式模板实例化

    我有几个模板类Impl 使用一些抽象方法 部分在 CPP 文件中实现 因此我需要显式实例化我的模板以便链接器找到它 如下所示 template class Impl
  • 使用 GROUP BY 连接单列字段

    有没有什么方法可以通过对字段进行分组来组合 连接一列中的字段 例如 col1 col2 1 aa 1 bb 1 cc 2 dd 2 ee 我想查询类似的内容 select col1 concat col2 from tableName gr
  • django 模板中的变量减法

    是可以写的 myval add 5 myval add value 乃至 myval add 5 但是 我不知道应该输入什么来添加值 1 之类的 myval add value 可悲的是 这不起作用 您需要使用双引号 myval add 5
  • 合并 XML 文档 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 目前不接受答案 我需要 合并 两个 XML 文档 覆盖重叠的属性和元素 例如 如果我有文件1
  • ConfigurationManager 不保存设置

    这是我正在使用的代码 private void SaveConfiguration if txtUsername Text txtPassword Text ConfigurationManager AppSettings Username
  • Netbeans - 安装 SASS

    我曾多次尝试在 netbeans 上安装 SASS 我已遵循以下教程 http kgagliardo com blog netbeans sass windows 7 创建并尝试保存 SASS 文件时出现以下错误 并且未创建 CSS 文件
  • 自定义 UIControl 和手势识别器

    我正在尝试创建一个类似于滑块的自定义 UIControl 该控件是一个视图的子视图 该视图还附加了一个点击手势识别器 现在的问题是这个点击手势识别器取消了发送到我的控件的触摸 有没有办法可以从我的控件的代码中覆盖它 如果我查看 iOS 中的
  • 如何使 macOS 框架可在 Nix 环境中 clang?

    我在 macOS 10 13 5 上学习 Rust 编程 并使用 Nix 来控制我的开发环境 一些行动 例如包括jsonwebtoken库或安装cargo watch模块 导致构建需要似乎未安装的 macOS 框架 我收到此错误消息 not
  • 在 VideoView 中播放视频时 Android 后退按钮不起作用

    在 VideoView 中播放视频时 Android 后退按钮不起作用 但它在播放视频之前有效 我正在为 VideoView 使用自定义 MediaController 我尝试使用调度按键事件 它不起作用 使用 VideoView 的 Ac
  • 将 ggplot2 颜色条刻度线更改为黑色

    在我的一些图中 我发现很难看到颜色条中的刻度线 我还没有找到改变蜱虫颜色的记录方法 所有示例似乎都集中在更改标签或根本不绘制刻度线 是否可以 Data require ggplot2 require grid n lt 100 x lt y
  • 给定一个时间,如何找到一个月前的时间

    给定一个时间 如何查找一个月前的时间 strtotime 1 month timestamp http php net manual en function strtotime php
  • Android:java.net.DatagramSocket.bind:无效参数异常

    背景 我正在编写一个简单的 UDP 应用程序来 ping 一个测试版服务器 我每分钟左右管理一次 告诉我它仍在运行 对于那些想知道的人 我无法在服务器上启用 ping 我计划在手机上运行此命令 以便在服务器不再响应时向我发出警告 我正在尝试
  • 如何将 Windows 的 EOL 设置为 LF,以便 API 通过 \n 获取值。不是 \r\n

    我使用 monaco editor create 方法来创建模型 问题是 monaco 正在将多行代码解析为 Windows 操作系统中的 r n 格式 我尝试在 monaco editor create 的 editorOptions 中
  • C++ IO 流简介

    我得到了一段代码本文我很困惑它是如何工作的 该片段开头写道 您可以通过测试读取结果来检测特定读取或写入操作是否失败 例如 要检查是否从用户读取了有效的整数 您可以执行以下操作 int x if cin gt gt x cout lt lt
  • Amazon S3 静态站点提供旧内容

    我的 S3 存储桶托管一个静态网站 我没有设置cloudfront 我最近更新了 S3 存储桶中的文件 当文件更新时 我在存储桶中手动确认 它仍然提供旧版本的文件 S3 上托管的静态网站是否存在某种缓存或版本控制 到目前为止我还没有找到任何
  • Backbone.js 在集合添加时触发渲染两次

    我正在使用 Todos 示例应用程序与最新版本的 Backbone 捆绑在一起 0 9 2 在学习 Backbone js 时 我的问题是 为什么应用程序设计为在将模型添加到 Todos 集合时触发渲染事件两次 如果我将此行放在 TodoV
  • 在数据库中存储性别(性别)

    我想以尽可能小的 大小 性能 成本将用户的性别存储在数据库中 到目前为止 我想到了 3 个场景 Int 与代码中的 Enum 对齐 1 男性 2 女性 3 char 1 Store m f或另一个单字符标识符 Bit 布尔值 该选项有合适的
  • ActionMailer 中的 Rails 3 Render Prawn pdf

    如何在ActionMailer中将大虾的pdf渲染为附件 我使用delayed job并且不明白 如何在操作邮件程序中 而不是在控制器中 渲染pdf文件 我应该使用什么格式 您只需要告诉 Prawn 将 PDF 渲染为字符串 然后将其作为附
  • Postgres 和 jsonb - 在任意键搜索值

    是否可以在 Postgres 的 JSONB 列中的任何键处查找给定值 在文档中我看不到任何例子 a 处的示例值JSONB column a 1 b 2 c 3 我想找到所有有的记录1作为任何地方的值 注意 可能还有其他键a b c目前未知
  • 往返数据的 Swift 数字类型

    Swift 3 倾向于Data代替 UInt8 我试图找出将各种数字类型 UInt8 Double Float Int64 等 编码 解码为数据对象的最有效 惯用的方法 There s 这个答案使用 UInt8 但它似乎使用了我在 Data