转载请注明本文地址:http://www.jianshu.com/p/1b3c8fc6995a
目的
在蓝牙项目的开发过程中,会遇到了一些与数据处理有关的问题,本文对这些问题进行了基本的整理并分享给大家。包含如下三个方面的内容。
数据大小端的介绍
大小端数据模式的转换
按位运算,左移、右移运算
一、数据大小端的介绍
网上关于数据大小端的介绍一大堆,为了让文章全面点,本文也就这方面简单说明一下。
a. 大小端表示数据在计算机中的存放顺序。
b. 大端模式符合人类的正常思维,高字节保存在内存的低地址。
c. 小端模式方便计算机处理,高字节保存在内存的高地址。
d. iOS中默认的是小端存储。
大小端示意图
你可以在Xcode中运行下面这两行代码,就会打印出大小端模式。
short int number = 0x8866; NSLog(@"%@",[NSString stringWithFormat:@"%x",((char *)&number)[0]].intValue == 66 ? @"小端模式" : @"大端模式");
二、大小端数据模式的转换
在讲解iOS大小端转换之前,先来回忆回忆基本的数据计量单位。
1字节是一个8位的数据,可以代表从0-255共256个数字。
蓝牙每次发过来的数据通常是一个32位的数据(根据协议来自行定义),也就是4个字节。
1B(byte,字节)= 8 bit(位)。
蓝牙通信的时候,从硬件接收到的数据是NSData类型,我们需要对数据进行解析才能拿到真正方便使用的数据。
但是接收到的数据在内存中的保存顺序可能与我们希望的相反,所以在解析的过程中就涉及到了大小端的转换问题。
其实iOS的大小端转换非常方便,在苹果的Core Fundation中就提供了进行这些数据处理的方法。Apple官方文档
下面我就举几个例子,一起来看一下Fundation中与大小端有关方法的基本使用。
1、CFByteOrderGetCurrent()
返回当前电脑的大小端模式
CFByteOrderGetCurrent() 返回的值是一个如下的枚举 enum __CFByteOrder { CFByteOrderUnknown, // 未知的 CFByteOrderLittleEndian, // 小端模式 CFByteOrderBigEndian // 大端模式 };
2、CFSwapInt16()
转换一个16位的整型数字
// 把数字15转换模式 CFSwapInt16(15) // 上面运算得到的结果十进制为3840,十六进制为0xF00。 // 而0xF00反转过来就是0xF = 15,所以证明这个方法确实对15进行了反转。
3、CFSwapInt16BigToHost()
把一个16位的整型数字从大端模式转为本机数据存放模式。如果本机为大端模式,则原值不变。
// 把大端模式的数字Number转为本机数据存放模式 CFSwapInt16BigToHost(Number)
4、CFSwapInt32HostToBig()
把一个32位本机模式数据转换为大端模式。如果本机为大端模式,则原值不变。
// 把本地存储模式的数字Number转为大端模式 CFSwapInt32HostToBig(Number)
还有好多方法(详见官方文档),基本都是大同小异,从字面就可以理解它的用法。
通常能用到的也就那么两三个。
一般需求是把大端转成本地模式,也就是小端模式。
CFSwapInt16BigToHost
CFSwapInt32BigToHost
下面是封装好了的两个方法,在开发中可以直接用来解析数据。
两个方法分别返回Signed和Unsigned类型的数据。
代码中的location代表准备解析的数据的位置,offset代表需要解析几位。
需要注意的是,当仅仅是解析1位数据的时候,就不需要使用像CFSwapInt16BigToHost这样的方法了,具体可以查阅代码。
// 转为本地大小端模式 返回Signed类型的数据 +(signed int)signedDataTointWithData:(NSData *)data Location:(NSInteger)location Offset:(NSInteger)offset { signed int value=0; NSData *intdata= [data subdataWithRange:NSMakeRange(location, offset)]; if (offset==2) { value=CFSwapInt16BigToHost(*(int*)([intdata bytes])); } else if (offset==4) { value = CFSwapInt32BigToHost(*(int*)([intdata bytes])); } else if (offset==1) { signed char *bs = (signed char *)[[data subdataWithRange:NSMakeRange(location, 1) ] bytes]; value = *bs; } return value; }
// 转为本地大小端模式 返回Unsigned类型的数据 +(unsigned int)unsignedDataTointWithData:(NSData *)data Location:(NSInteger)location Offset:(NSInteger)offset { unsigned int value=0; NSData *intdata= [data subdataWithRange:NSMakeRange(location, offset)]; if (offset==2) { value=CFSwapInt16BigToHost(*(int*)([intdata bytes])); } else if (offset==4) { value = CFSwapInt32BigToHost(*(int*)([intdata bytes])); } else if (offset==1) { unsigned char *bs = (unsigned char *)[[data subdataWithRange:NSMakeRange(location, 1) ] bytes]; value = *bs; } return value; }
三、按位运算,左移、右移运算
当拿到了一个大小端转换完毕的数据,又会面临下一个问题,这个数据中不同的位可能代表了不同的意义(根据与硬件工程师的协议来定义),如何继续拿到对应位的数字呢?这时候,按位运算、左移右移就派上用场了。
1、按位与 &
同为1为1,否则为0
例如:3 & 5
0000 0011
0000 0101
0000 0001 = 1
所以 3 & 5=1
特点:
(1)清零:任何数和0相与,结果为0.
(2)取出指定位的值。取哪一位,就把对应的位定为1。
例如:
我们拿到一个16位的数据X,这个数据的低4位代表了版本号。
就可以利用按位与来拿到这个数字,代码如下
// 0x0f的二进制为0000 1111,所以就取出了低4位的数据 int result = X & 0x0f;
2、按位或 |
只要有一个为1就为1
负数按补码的形式参加按位或运算
例如:3 | 5
0000 0011
0000 0101
0000 0111 = 7
所以 3 | 5=7
特点:
(1)对数据的某些位置1。
例如:
将X=1010 0000的后四位置1
1010 0000
0000 1111
1010 1111
这样后4位就全为1了
3、异或运算 ^
如果对应的位不同则为1,相同为0
例如 3 ^ 5
0000 0011
0000 0101
0000 0110
所以 3 ^ 5= 6
特点:
(1)特定位翻转,哪一位需要翻转就把对应的位设置为1
(2)任何数和0异或,原值不变。
(3)异或运算可以交换位置:3 ^ 5 ^ 6 == 3 ^ 6 ^ 5
(4)相同的数异或等于0:9 ^ 9 == 0
(5)a ^ b ^ a == b
4、取反 ~
0变1,1变0
例如 ~3
0000 0011
1111 1100
特点:
(1)配合按位与把一个数的最低位设置为0
例如:
把X=1011 0111按位与(~1)
X & (~1) = 1011 0110
这样最后一位就为0了
5、左移运算 <<
二进制位全部左移若干位,左边的丢弃,右边补0
例如 3<<2
0000 0011 = 3
0000 1100 = 12 (左移后)
左移3<<2 == 12
特点:
若左移时舍弃的最高位不包含1,则每左移一位,就乘以一次2.
所以a<
6、右移运算 >>
二进制右移若干位,正数左边补0,负数左边补1,右边丢弃。
例如 12>>2
0000 1100 = 12
0000 0011 = 2 (右移后)
右移12>>2 == 3
特点:
每右移一位,就除以一次2.
a>>n 就是 a除以2的n次方
例如:
继续用上面按位与的例子,
我们拿到一个16位的数据X,这次版本号不是低4位了,而是5-8位,该如何处理呢?
这种情况左移或右移就派上用场了。
代码如下
// 0xf0的二进制为1111 0000,与X按位与之后,就取出了5-8位。 // 然后右移4位,最终得到了所需要的数据。 int result = (X & 0xf0) >> 4;
后记
蓝牙通信,数据处理是一个很重要的问题,实际开发过程中需要结合具体情境和协议来进行相关操作。
推荐简单又好用的分类集合:WHKit
github地址:https://github.com/remember17