ios中不一定需要c构建协议。但是总体来说用c语言来说相对对数据处理友好方便一些,不像oc那样笨重,毕竟c是做底层的。但是用了c处理有时候需要考虑内存释放问题。话不多说。。。
如果某些协议中只出现8位的数据,这种是相对最好处理的。例如:
如下协议:
可以构建:
typedef struct{
uint8_t head;
uint8_t data_1;
uint8_t data_2;
uint8_t place[2];
uint8_t check;
uint8_t tail;
}Test_struct;
对于这种类型的数据要转成oc的NSdata对象可用
Test_struct struct_data_context;
NSData *data = [NSData dataWithBytes:&struct_data_context length:sizeof(Test_struct)];
那么NSdata转该类型的结构体可用
Test_struct test_struct_data ;
[data getBytes:&test_struct_data length:sizeof(Test_struct)];
是不是很简单?不用一个一个赋值了!
但是。。。如果数据中存在16位数据 (数据分高低8位)这种情况下需要注意一下几点
1.数据大小端
2.结构体对齐(
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
)详细的后面会提到
例如:以下协议格式(小端)
我们可以这样构建结构体,
typedef struct{ uint8_t head; uint8_t data_1;
uint16_tdata; uint8_t place[2]; uint8_t check; uint8_t tail;}Test_struct;
注意删除线。对于16位数据小端处理的数据,(上表第3第4字节)ios 处理器arm架构 默认处理数据都是小端传输的, uint16_t 数据映射到数组里就是低8位在前,高8位在后 形如{低8位,高8位 } 的数组,到这里大家是不是觉得很简单。NO NO NO,too 🐑 了。。。。
莫急,我们可以测试打印的 sizeof (Test_struct)= 8;
下面比较下面的协议:
我们如果按照上面逻辑我们会这样构建
typedef struct{ uint8_t head; uint16_t data; uint8_t place[2]; uint8_t check; uint8_t tail;}Test_struct;
但是如果你打印一下 size 的话,你会发现sizeof(Test_struct)还是等于 8;明明比上面少一位怎么还是8位?
原因是 根据结构体原则。第二位成员2个字节,第二位成员内存从结构体开始 2字节的倍数位开始排。所以 第一个成员占了第1字节 还有个补位第二个字节,uint16个低8位占了第三个字节, uint16个高8位占了第四个字节。加上后面的4个字节,一共8个字节,第三原则(最大成员字节的整数陪)总字节数8的确是 最大成员2字节的整数倍。
那这样的话我们怎样构建结构体方便些呢?
1 。第一种把所有结构体 改成一个字节 ,意思是把uint16 高低字节拆开。(常规做法)。
2。结构体补位
第一种就不用说了,我们试下第二种怎么做。
typedef struct{ uint8_t head;
uint8_t space; uint16_t data; uint8_t place[2]; uint8_t check; uint8_t tail; }Test_struct;
注意删除线。我们在head 后面补了一位。补了一位数据自然有出入,莫急。我们最后生成的时候把这一位删除数据就对了。当然方法有很多(数组遍历赋值等等),我就用内存拷贝补位前和补位后的内存这种方法。
NSData * struct2NSdata(Test_struct *context) {
int temp_size = sizeof(Test_struct) - 1; //有效的数据字节数
uint8_t *temp_context = (uint8_t *)context; //强转结构体指针类型,为了后面一个字节的偏移量
uint8_t *temp = malloc(temp_size);//分配内存
memset(temp, 0, temp_size); //内存块初始化0
memcpy(temp, temp_context, 1);//拷贝第一位;
memcpy(temp + 1,temp_context + 2, temp_size - 1); //拷贝context第3位至结尾
NSData *result = [NSData dataWithBytes:temp length:temp_size]; //最终生成的data
free(temp); //释放开辟的内存
return result;
}
对于NSData 转结构体也是一个办法补一位 哈哈
void nsdata2Struct(NSData *data , Test_struct *context) {
int temp_size = data.length; //有效的数据字节数
uint8_t *temp_context = context; //强转结构体
uint8_t *head_data = malloc(1);
[data getBytes:head_data range:NSMakeRange(0, 1)]; //储存前内存块
uint8_t *tail_data = malloc(temp_size - 1);
[data getBytes:head_data range:NSMakeRange(1, temp_size - 1)]; //储存后面内存块
memcpy(temp_context, head_data, 1); //拷贝前内存
memcpy(temp_context + 2, tail_data, data.length - 1); //拷贝后面的内存块 到第三字节至结尾 (忽略第二位)
free(head_data); //释放
free(tail_data);
}
还有结构体 第三原则 必须是最大成员的整数倍,如果不是会在结构体最后一位补位。 处理方法也和上面类似。
综上,如果数据都是一个字节数据,构建可以什么都不用考虑,
如果数据为小端传输,可以用上面方法,考虑一下结构体对齐,不对齐需要补位,优点是,数据转换的时候不需要一个个赋值,反正我是写的手疼。
如果数据为大端传输,还是老老实实把成员都变成一个字节的成员变量,或者写个函数高低位转换。在用上面所提的方法。