在藍牙項目的開發過程當中,會遇到了一些與數據處理有關的問題,本文對這些問題進行了基本的整理並分享給你們。包含以下三個方面的內容。git
- 數據大小端的介紹
- 大小端數據模式的轉換
- 按位運算,左移、右移運算
###1、數據大小端的介紹 網上關於數據大小端的介紹一大堆,爲了讓文章全面點,本文也就這方面簡單說明一下。github
a. 大小端表示數據在計算機中的存放順序。 b. 大端模式符合人類的正常思惟,高字節保存在內存的低地址。 c. 小端模式方便計算機處理,高字節保存在內存的高地址。 d. iOS中默認的是小端存儲。app
你能夠在Xcode中運行下面這兩行代碼,就會打印出大小端模式。ui
short int number = 0x8866;
NSLog(@"%@",[NSString stringWithFormat:@"%x",((char *)&number)[0]].intValue == 66 ? @"小端模式" : @"大端模式");
複製代碼
###2、大小端數據模式的轉換spa
藍牙通訊的時候,從硬件接收到的數據是NSData類型,咱們須要對數據進行解析才能拿到真正方便使用的數據。 可是接收到的數據在內存中的保存順序可能與咱們但願的相反,因此在解析的過程當中就涉及到了大小端的轉換問題。code
其實iOS的大小端轉換很是方便,在蘋果的Core Fundation中就提供了進行這些數據處理的方法。Apple官方文檔orm
下面我就舉幾個例子,一塊兒來看一下Fundation中與大小端有關方法的基本使用。cdn
一、CFByteOrderGetCurrent() 返回當前電腦的大小端模式blog
CFByteOrderGetCurrent()
返回的值是一個以下的枚舉
enum __CFByteOrder {
CFByteOrderUnknown, // 未知的
CFByteOrderLittleEndian, // 小端模式
CFByteOrderBigEndian // 大端模式
};
複製代碼
二、CFSwapInt16() 轉換一個16位的整型數字內存
// 把數字15轉換模式
CFSwapInt16(15)
// 上面運算獲得的結果十進制爲3840,十六進制爲0xF00。
// 而0xF00反轉過來就是0xF = 15,因此證實這個方法確實對15進行了反轉。
複製代碼
三、CFSwapInt16BigToHost() 把一個16位的整型數字從大端模式轉爲本機數據存放模式。若是本機爲大端模式,則原值不變。
// 把大端模式的數字Number轉爲本機數據存放模式
CFSwapInt16BigToHost(Number)
複製代碼
四、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;
}
複製代碼
###3、按位運算,左移、右移運算 在講解位運算和左右移以前,先來回憶回憶基本的數據計量單位。
1字節是一個8位的數據,能夠表明從0-255共256個數字。 1B(byte,字節)= 8 bit(位)。
模擬一次解析數據的過程:
假如藍牙每次發過來的數據大小爲32個字節,這個數據在NSData類型下Log出來是這個樣子:<0aa60000 00000000 00000000 00000000 00000000 00000059 9db56800 00260b01>
每兩個數字表示一個十六進制的數據,例如最左邊的0a表明了一個字節,也就是0x0A = 10。
如今咱們要截取最左邊的0aa6這兩個字節(16位),這個數據是UInt16類型,那麼首先要作的就是運用上面封裝好了的大小端轉換方法來截取這兩個字節,下面代碼中的result就是所須要的數據。
// 從第0位開始,截取2個字節,因此location是0,offset是2
UInt16 result = [self unsignedDataTointWithData:data Location:0 Offset:2];
複製代碼
但是拿到result以後工做尚未結束。
這時候,位運算就派上用場了。 一塊兒來看看位運算和左右移的基本使用方法和情景,需求的答案也在其中。
####一、按位與 & 同爲1爲1,不然爲0
例如:3 & 5 0000 0011 0000 0101 0000 0001 = 1
因此 3 & 5=1
特色: (1)清零:任何數和0相與,結果爲0. (2)取出指定位的值。取哪一位,就把對應的位定爲1。
例如: 拿到了一個16位的數據result = 0000 1010 1010 0110,如何拿到這個數據的低4位呢? 就可使用按位與,代碼以下
// 0x000f == 0000 0000 0000 1111
// 按位與上result以後,獲得的number == 0000 0000 0000 0110 就是低4位的數據0110
int number = result & 0x000f;
複製代碼
####二、按位或 | 只要有一個爲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了
####三、異或運算 ^ 若是對應的位不一樣則爲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
####四、取反 ~ 0變1,1變0
例如 ~3 0000 0011 1111 1100
特色: (1)配合按位與把一個數的最低位設置爲0 例如: 把X=1011 0111按位與(~1) X & (~1) = 1011 0110 這樣最後一位就爲0了
####五、左移運算 << 二進制位所有左移若干位,左邊的丟棄,右邊補0
例如 3<<2 0000 0011 = 3 0000 1100 = 12 (左移後) 左移3<<2 == 12
特色: 若左移時捨棄的最高位不包含1,則每左移一位,就乘以一次2. 因此a<<n 就是 a乘以2的n次方
####六、右移運算 >> 二進制右移若干位,正數左邊補0,負數左邊補1,右邊丟棄。
例如 12>>2 0000 1100 = 12 0000 0011 = 2 (右移後) 右移12>>2 == 3
特色: 每右移一位,就除以一次2. a>>n 就是 a除以2的n次方
例如: 繼續用上面按位與的例子, 拿到了一個16位的數據result = 0000 1010 1010 0110,如何拿到這個數據的5-8位呢? 首先運用按位與把5-8位以外的數據所有置0,而後用右移來拿到具體數值。 代碼以下
// 0x00f0 == 0000 0000 1111 0000,result按位與0xf0以後,結果爲0000 0000 1010 0000
// 而後右移4位,獲得最終所須要的數據number == 0000 0000 0000 1010
int number = (result & 0x00f0) >> 4;
複製代碼
藍牙通訊,數據處理是一個很重要的問題,實際開發過程當中須要結合具體情境和協議來進行相關操做。
!轉載請註明本文地址:www.jianshu.com/p/1b3c8fc69…