在Swift中将数据读入结构

Jan*_*Jan 3 swift

我正在尝试使用该方法将其读Data入Swift 4 .问题structwithUnsafeBytes

网络UDP数据包具有以下格式:

data: 0102 0A00 0000 0B00 0000

01           : 1 byte : majorVersion      (decimal 01)
02           : 1 byte : minorVersion      (decimal 02)
0A00 0000    : 4 bytes: applicationHostId (decimal 10)
0B00 0000    : 4 bytes: versionNumber     (decimal 11)
Run Code Online (Sandbox Code Playgroud)

然后我有一个扩展Data,需要一个startlength几个字节来读取

extension Data {
    func scanValue<T>(start: Int, length: Int) -> T {
        return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
    }
}
Run Code Online (Sandbox Code Playgroud)

这在逐个读取值时正常工作:

// correctly read as decimal "1"
let majorVersion: UInt8 = data.scanValue(start: 0, length: 1)

// correctly read as decimal "2"
let minorVersion: UInt8 = data.scanValue(start: 1, length: 1)

// correctly read as decimal "10"
let applicationHostId: UInt32 = data.scanValue(start: 2, length: 4)

// correctly read as decimal "11"
let versionNumber: UInt32 = data.scanValue(start: 6, length: 4)
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个struct代表整个数据包的如下

struct XPLBeacon {
    var majorVersion: UInt8        // 1 Byte
    var minorVersion: UInt8        // 1 Byte
    var applicationHostId: UInt32  // 4 Bytes
    var versionNumber: UInt32      // 4 Bytes
}
Run Code Online (Sandbox Code Playgroud)

但是当我直接将数据读入结构时,我遇到了一些问题:

var beacon: XPLBeacon = data.scanValue(start: 0, length: data.count)

// correctly read as decimal "1"
beacon.majorVersion

// correctly read as decimal "2"
beacon.minorVersion

// not correctly read
beacon.applicationHostId

// not correctly read
beacon.versionNumber
Run Code Online (Sandbox Code Playgroud)

我应该工作解析这样的整个结构?

Mar*_*n R 7

从数据中读取整个结构不起作用,因为结构成员被填充到它们的自然边界。的内存布局struct XPLBeacon

 AB xx CCCCDDDD

在哪里

 抵消成员
  0 A - 主要版本 (UInt8)
  1 B - 次要版本 (UInt8)
  2 xx - 填充
  4 CCCC - applicationHostId (UInt32)
  8 DDDD - 版本号 (UInt32)

并插入填充,以便UInt32成员与内存地址对齐,内存地址是其大小的倍数。这也得到了证实

print(MemoryLayout<XPLBeacon>.size) // 12
Run Code Online (Sandbox Code Playgroud)

(有关 Swift 中对齐的更多信息,请参阅 类型布局)。

如果将整个数据读入结构体,则字节分配如下

 01 02 0A 00 00 00 0B 00 00 00
 AB xx CCCCDDDD

这就解释了为什么major/minorVersion是正确的,但applicationHostIdversionNumber 是错误的。从数据中单独读取所有成员是正确的解决方案。


Leo*_*bus 6

由于雨燕3数据符合RandomAccessCollection,MutableCollection,RangeReplaceableCollection.因此,您只需创建一个自定义初始值设定项来初始化您的struct属性,如下所示:

struct XPLBeacon {
    let majorVersion, minorVersion: UInt8             // 1 + 1 = 2 Bytes
    let applicationHostId, versionNumber: UInt32      // 4 + 4 = 8 Bytes
    init(data: Data) {
        self.majorVersion      = data[0...0].withUnsafeBytes { $0.pointee }
        self.minorVersion      = data[1...1].withUnsafeBytes { $0.pointee }
        self.applicationHostId = data[2...5].withUnsafeBytes { $0.pointee }
        self.versionNumber     = data[6...9].withUnsafeBytes { $0.pointee }
    }
}
Run Code Online (Sandbox Code Playgroud)
let data = Data([0x01,0x02, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00,0x00])
print(data as NSData)     // "<01020a00 00000b00 0000>\n"

let beacon = XPLBeacon(data: data)
beacon.majorVersion       // 1
beacon.minorVersion       // 2
beacon.applicationHostId  // 10
beacon.versionNumber      // 11
Run Code Online (Sandbox Code Playgroud)