我正在构建一个 Data
看起来像这样的对象:
struct StructuredData {
var crc: UInt16
var someData: UInt32
var someMoreData: UInt64
// etc.
}
我正在运行一个从字节 2 开始并处理长度 12 的 CRC 算法。
返回CRC时,必须存在于Data
的开头目的。在我看来,我的选择是:
生成
Data
不包含 CRC 的对象,对其进行处理,然后构建另一个Data
对象(这样我现在拥有的 CRC 值将位于Data
对象的开头。生成数据对象以包含一个清零的 CRC 作为开始,然后在
[0..<2]
范围内对数据进行变异.
显然,2 会更可取,因为它使用更少的内存和更少的处理,但我不确定是否需要这种类型的优化。我还是宁愿选择 2,除非我不知道如何在给定的索引范围内改变数据。非常感谢任何帮助。
最佳答案
我不推荐使用这种改变 Data
的方式:
data.replaceSubrange(0..<2, with: UnsafeBufferPointer(start: &self.crc, count: 1))
请试试这个:
data.replaceSubrange(0..<2, with: &self.crc, count: 2)
很难解释为什么,但我会尝试......
在 Swift 中,inout
参数以复制输入复制输出语义工作。当你写这样的东西时:
aMethod(¶m)
Swift 分配一些足够大的区域来容纳
param
的内容,将
param
复制到区域中,(copy-in)通过传递区域地址调用方法,
并且当从调用返回时,将该区域的内容复制回
param
(复制输出)。
在许多情况下,Swift 通过仅传递 param
的实际地址来优化(甚至可能在 -Onone
设置中发生)这些步骤,但没有明确记录.
因此,当 inout
参数被传递给 UnsafeBufferPointer
的初始化器时,UnsafeBufferPointer
接收到的地址可能指向一个时间区域, 会在初始化完成后立即释放。
因此,replaceSubrange(_:with:)
可以将已释放区域中的字节复制到Data
中。
我相信第一个代码在这种情况下会起作用,因为 crc
是一个结构的属性,但如果有一个简单而安全的替代方案,您最好避免不安全的方式。
对 Brandon Mantzey 自己的回答的评论的补充。
data.append(UnsafeBufferPointer(start: &self.crcOfRecordData, count: 1))
在上面的意思中使用safe。这不是安全,原因与上述相同。
我会这样写:
data.append(Data(bytes: &self.crcOfRecordData, count: MemoryLayout<UInt16>.size))
(假设crcOfRecordData
的类型为UInt16
。)
如果您不喜欢创建额外的 Data
实例,您可以将其写为:
withUnsafeBytes(of: &self.crcOfRecordData) {urbp in
data.append(urbp.baseAddress!.assumingMemoryBound(to: UInt8.self), count: MemoryLayout<UInt16>.size)
}
注释中没有提到这一点,但在上面安全的含义中,下面这行不是安全。
let uint32Data = Data(buffer: UnsafeBufferPointer(start: &self.someData, count: 1))
都是一样的道理。
我会这样写:
let uint32Data = Data(bytes: &self.someData, count: MemoryLayout<UInt32>.size)
尽管如此,可观察到的意外行为可能会在某些非常有限的条件下发生,而且概率很小。
这种行为只有在满足以下两个条件时才会发生:
Swift 编译器生成非优化的拷贝入拷贝出代码
在非常狭窄的时间段之间,由于时间区域被释放直到
append
方法(或Data.init
)完成复制整个内容,区域被修改用于其他用途。
在当前的 Swift 实现中,条件 #1 仅在有限的情况下变为真。
条件 #2 很少只在多线程环境中发生。 (不过,Apple 的框架使用了许多隐藏线程,您可以在 Xcode 的调试器中找到它。)
事实上,我还没有看到关于上述不安全案例的任何问题,我的安全可能有点矫枉过正。
但是替代安全代码并不那么复杂,对吗? 在我看来,您最好习惯使用all-cases-safe代码。
关于Swift 在范围内改变数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52503006/