我正在构建一个看起来像这样的Data
对象:
struct StructuredData {
var crc: UInt16
var someData: UInt32
var someMoreData: UInt64
// etc.
}
我正在运行的CRC算法将从字节2开始,进程长度为12。
返回CRC时,它必须存在于Data
对象的开头。如我所见,我的选择是:
生成不包含CRC的Data
对象,对其进行处理,然后构建另一个包含该对象的Data
对象(这样,我现在拥有的CRC值将位于Data
对象的开始。
生成数据对象以包含以零开始的CRC开头,然后对[0..<2]
范围内的数据进行突变。
显然,2是更可取的,因为它使用更少的内存和更少的处理,但是我不确定这种类型的优化是否是必要的。我还是宁愿选择2,除了我不知道如何在给定的索引范围内对数据进行突变。任何帮助将不胜感激。
答案 0 :(得分:1)
我不建议使用以下方法来突变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
复制到该区域(复制)
调用该方法并传递区域地址,
,当从呼叫返回时,将该区域的内容复制回param
(复制)。
在许多情况下,Swift仅通过传递-Onone
的实际地址来优化步骤(即使在param
设置中也可能发生),但没有明确记录。
因此,当将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)
}
此内容未在评论中提及,但按照上述 safe 的含义,以下行不是 safe 。
let uint32Data = Data(buffer: UnsafeBufferPointer(start: &self.someData, count: 1))
出于同样的原因。
我将其写为:
let uint32Data = Data(bytes: &self.someData, count: MemoryLayout<UInt32>.size)
尽管如此,可观察到的意外行为可能会在非常有限的条件下发生,并且发生的可能性很小。
仅当满足以下两个条件时,才会发生这种行为:
Swift编译器会生成未优化的拷贝复制拷贝代码
在非常狭窄的时间段之间,由于暂时区域被释放,直到append
方法(或Data.init
)完成了对整个内容的复制,所以对该区域进行了修改以供其他用途。
条件#1仅在当前Swift实施中的有限情况下成立。
条件2仅在多线程环境中很少发生。 (尽管,Apple的框架使用许多隐藏的线程,正如您在Xcode的调试器中可以找到的一样。)
实际上,对于上述不安全案件,我没有看到任何疑问,我的 safe 可能是 overkill 。
但是替代的 safe 代码并不那么复杂,对吗? 我认为,您最好习惯于使用 all-cases-safe 代码。
答案 1 :(得分:0)
我知道了。实际上,我遇到了一个语法错误,这使我感到困惑,因为我之前从未见过。
这是答案:
data.replaceSubrange(0..<2, with: UnsafeBufferPointer(start: &self.crc, count: 1))