我在iOS中做了一堆BLE,这意味着大量紧凑的C结构被编码/解码为字节包。下面的游乐场片段说明了我试图做的一般事情。如何从镜像内省改变孩子的值
import Foundation
// THE PROBLEM
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
}
sizeof(Thing) // --> 9 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 13> :(
因此,给定一系列不同大小的字段,我们没有得到“最紧密”的字节打包。众所周知并被接受。鉴于我的简单结构,我希望能够无需填充或对齐的东西就可以任意编码字段。实际上比较容易:
// ARBITRARY PACKING
var mirror = Mirror(reflecting: thing)
var output:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as UInt32:
(0...3).forEach { output.append(UInt8((value >> ($0 * 8)) & 0xFF)) }
case let value as UInt8:
output.append(value)
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
output.count // --> 6 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de13> :)
哈扎!按预期工作。可能可能会添加一个类,或者可能是一个协议扩展,并有一个很好的实用工具。我要对付的问题是相反的过程:
// ARBITRARY DEPACKING
var input = output.generate()
var thing2 = Thing()
"\(thing2.a), \(thing2.b), \(thing2.c)" // --> "0, 0, 0"
mirror = Mirror(reflecting:thing2)
mirror.children.forEach { (label, child) in
switch child {
case let oldValue as UInt8:
let newValue = input.next()!
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
case let oldValue as UInt32: // do little endian
var newValue:UInt32 = 0
(0...3).forEach {
newValue |= UInt32(input.next()!) << UInt32($0 * 8)
}
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
default:
print("skipping field \(label) of type \(child.dynamicType)")
}
}
给定一个无人居住的结构值,我可以字节流进行相应的译码,找出新的价值会是怎样为每个字段。我不知道该怎么做的是实际用新值更新目标结构。在上面的例子中,我展示了如何用C实现,获取指向原始子元素的指针,然后用新值更新它的值。我可以在Python/Smalltalk/Ruby中轻松完成。但我不知道在Swift中如何做到这一点。
UPDATE
正如意见建议,我可以做类似如下:
// SPECIFIC DEPACKING
extension GeneratorType where Element == UInt8 {
mutating func _UInt8() -> UInt8 {
return self.next()!
}
mutating func _UInt32() -> UInt32 {
var result:UInt32 = 0
(0...3).forEach {
result |= UInt32(self.next()!) << UInt32($0 * 8)
}
return result
}
}
extension Thing {
init(inout input:IndexingGenerator<[UInt8]>) {
self.init(a: input._UInt8(), b: input._UInt32(), c: input._UInt8())
}
}
input = output.generate()
let thing3 = Thing(input: &input)
"\(thing3.a), \(thing3.b), \(thing3.c)" // --> "66, 3735928495, 19"
基本上,我谨各种流解码方法的字节流(即GeneratorType其中元素== UINT8) ,然后我只需要编写一个初始化程序,将这些字符串以相同的顺序排序并将结构定义为。我猜那部分本质上是“复制”了结构定义本身(因此容易出错),这是我希望用某种内省来处理的。镜子是我意识到的唯一真正的斯威夫特反思,它似乎相当有限。
我可能只是天真的,但我不太明白这与镜像,打包或其他任何事情有关。结构是值类型。在'mirror.children.forEach'中,'child'实际上是一个副本。那么你怎么能通过“孩子”的方式回复原作?你没有原始的。 – matt
我刚到的地方也是如此。此外,即使它是一个班级,这些也可能是“让”值。没有任何承诺,这是合法的。我可能会换一种方式,而不是尝试在这里使用镜像。您可能需要编写更多的代码,但应该可以使其非常机械。 –
不,我不认为你天真@matt :)我想要一个更好的问题标题,你有一个建议?通常,在我写出问题时,标题变得更清晰。但有时候并非如此。 –