如何将 NSData 准确转换为 struct
How to convert NSData to struct accurately
我从设备(BLE)获得数据:<840100ec d5045715 00010014 00240018 00>
但是第二个字节无法准确转换。喜欢这些:
但是我可以使用 Uint8 数组,为什么?谢谢。
像这样的代码:
// I got the data:<840100ec d5045715 00010014 00240018 00>
case SPK_FEEDBACK_HistoryDataPort:
// Log
NSLog(@"receive data:%@", [NSData dataWithBytes:originalCommandBytes length:sizeof(D2MHistoryDataPort)]);
// originalCommandBytes dataType:UInt8 *
D2MHistoryDataPort *historyData = (D2MHistoryDataPort *)originalCommandBytes;
// Log
NSLog(@"收到硬件返回的0x%x指令(历史数据体): 历史数据包的索引:%d; 时间戳:%d; 步数:%d; 卡路里:%d; 距离:%d; 睡眠:%d; 运动时长:%d",
historyData->cmd,
historyData->index,
(unsigned int)historyData->timeStamp,
historyData->steps,
historyData->calories,
historyData->distance,
historyData->sleep,
historyData->duration);
break;
// I declare this struct in another class
typedef struct {
UInt8 cmd;
UInt16 index;
UInt32 timeStamp;
UInt16 steps;// 步数
UInt16 calories;// 卡路里
UInt16 distance;// 距离,单位m
UInt16 sleep;// 睡眠
UInt16 duration;// 运动时长,单位minute
} D2MHistoryDataPort;
编译器如何在内存中布置结构的各个字段取决于实现。通常编译器必须添加填充以正确对齐字段,它甚至可能会重新排序它们(通过对相同大小的字段进行分组)以减少所需的填充和结构的整体大小。
您可以使用 __attribute__((packed))
:
关闭此行为
typedef struct __attribute__((packed)) {
UInt8 cmd;
UInt16 index;
UInt32 timeStamp;
UInt16 steps;// 步数
UInt16 calories;// 卡路里
UInt16 distance;// 距离,单位m
UInt16 sleep;// 睡眠
UInt16 duration;// 运动时长,单位minute
} D2MHistoryDataPort;
你做的肯定是行不通的。您正在尝试采用一个结构,假设您可以将其解释为一个字节序列,将其写入并读回。那是行不通的。从在编译器版本之间、32 位和 64 位编译器之间等具有不同布局的结构开始。人们在 90 年代就知道这是个坏主意。
使用 NSCoding 协议。或者将数据转换为JSON。不要试图将结构解释为字节序列。
如果您绝对无法避免使用 NSData,那么它的安全工作方式如下:
第一步:定义外部数据格式。外部数据格式不是 "whatever the compiler decided to layout my struct"。外部数据格式为"One unsigned byte cmd; two unsigned bytes index, most significant byte first. 4 unsigned bytes time stamp, most significant byte first, meaning the number of seconds since Jan 1st 1904, ... "等。
然后读取结构,获取指向第一个字节的指针,检查您是否有足够的字节,然后写入
mystruct.cmd = p [0];
mystruct.index = (p [1] << 8) | p [2];
mystruct.timeStamp = (p [3] << 24) | (p [4] << 16) ...
等等。
我从设备(BLE)获得数据:<840100ec d5045715 00010014 00240018 00>
但是第二个字节无法准确转换。喜欢这些:
但是我可以使用 Uint8 数组,为什么?谢谢。
像这样的代码:
// I got the data:<840100ec d5045715 00010014 00240018 00>
case SPK_FEEDBACK_HistoryDataPort:
// Log
NSLog(@"receive data:%@", [NSData dataWithBytes:originalCommandBytes length:sizeof(D2MHistoryDataPort)]);
// originalCommandBytes dataType:UInt8 *
D2MHistoryDataPort *historyData = (D2MHistoryDataPort *)originalCommandBytes;
// Log
NSLog(@"收到硬件返回的0x%x指令(历史数据体): 历史数据包的索引:%d; 时间戳:%d; 步数:%d; 卡路里:%d; 距离:%d; 睡眠:%d; 运动时长:%d",
historyData->cmd,
historyData->index,
(unsigned int)historyData->timeStamp,
historyData->steps,
historyData->calories,
historyData->distance,
historyData->sleep,
historyData->duration);
break;
// I declare this struct in another class
typedef struct {
UInt8 cmd;
UInt16 index;
UInt32 timeStamp;
UInt16 steps;// 步数
UInt16 calories;// 卡路里
UInt16 distance;// 距离,单位m
UInt16 sleep;// 睡眠
UInt16 duration;// 运动时长,单位minute
} D2MHistoryDataPort;
编译器如何在内存中布置结构的各个字段取决于实现。通常编译器必须添加填充以正确对齐字段,它甚至可能会重新排序它们(通过对相同大小的字段进行分组)以减少所需的填充和结构的整体大小。
您可以使用 __attribute__((packed))
:
typedef struct __attribute__((packed)) {
UInt8 cmd;
UInt16 index;
UInt32 timeStamp;
UInt16 steps;// 步数
UInt16 calories;// 卡路里
UInt16 distance;// 距离,单位m
UInt16 sleep;// 睡眠
UInt16 duration;// 运动时长,单位minute
} D2MHistoryDataPort;
你做的肯定是行不通的。您正在尝试采用一个结构,假设您可以将其解释为一个字节序列,将其写入并读回。那是行不通的。从在编译器版本之间、32 位和 64 位编译器之间等具有不同布局的结构开始。人们在 90 年代就知道这是个坏主意。
使用 NSCoding 协议。或者将数据转换为JSON。不要试图将结构解释为字节序列。
如果您绝对无法避免使用 NSData,那么它的安全工作方式如下:
第一步:定义外部数据格式。外部数据格式不是 "whatever the compiler decided to layout my struct"。外部数据格式为"One unsigned byte cmd; two unsigned bytes index, most significant byte first. 4 unsigned bytes time stamp, most significant byte first, meaning the number of seconds since Jan 1st 1904, ... "等。
然后读取结构,获取指向第一个字节的指针,检查您是否有足够的字节,然后写入
mystruct.cmd = p [0];
mystruct.index = (p [1] << 8) | p [2];
mystruct.timeStamp = (p [3] << 24) | (p [4] << 16) ...
等等。