Swift中基本数据类型与NSData转换
最近由于程序的需要,要与JAVA的服务端进行Socket的交互,那么这就牵涉到了数据的交互.Socket的数据交互一般都是直接采用二进制Bytes的方式来传递,那么就需要把Swift中的各种基本数据转换成为JAVA服务器可以认可的Bytes字节数组,以及把JAVA的字节数组反序列化为Swift中的基本数据.
big-endian and little-endian
要在不同程序中进行字节数组的数据交换,有个很重要的东西就是字节序
.字节序
顾名思义就是字节的顺序,也就是大于1个字节类型的数据在内存中存放的顺序.这个在跨平台以及网络程序交互中非常的重要.
常见的字节序主要有两类:Big-Endian
和Little-Endian
.它们的定义为:
- Big-Endian:高位字节排放在内存的低地址端,地位字节排放在内存的高地址端.
- Little-Endian:地位字节排放在内存的低地址端,高位字节排放在内存的高地址端.
这样说可能比较抽象,我们来举个例子就非常清楚了:
比如一个32位的Int类型数据:let a = Int32(2)
分别采用Big-Endian
和Little-Endian
的情况如下:
字节号 | 0 | 1 | 2 | 3 |
---|---|---|---|---|
Big-endian | 00 | 00 | 00 | 02 |
Little-Endian | 02 | 00 | 00 | 00 |
也就是他们两个是相反的.Big-Endian
和Little-Endian
跟CPU的指令有关,每一种CPU不是Big-Endian
就是Little-Endian
.常见的IA架构的CPU,比如Intel或AMD的都是使用的Little-Endian
,而PowerPC活着SPARC的处理器则是Big-Endian
的.而在互联网的网络交互以及TCP协议中使用的是Big-Endian
,JAVA的虚拟机中的字节序是Big-Endian
的.而Swift由于是运行在IA架构的CPU上的,因此,它的字节序是Little-Endian
的.
正是由于运行在X86上的Swift
和JAVA
的字节序是相反的,因此,它们两个进行跨语言的网络数据交互的时候,就需要对数据进行字节序的转换.否则就会出现数据读取错误的情况,比如用JAVA
采用Big-Endian
序的Int3202000000
,Swift
采用Little-Endian
序解析出来是33554432
而不是期望的2
.
在Swift中,Apple在CoreFoundation
中提供了一些列的函数来提供字节序的转换.它们都在CFByteOrder
中有所定义:
|
|
使用这些函数就可以对字节序进行转换. 更多的可以参考Apple的官方手册
2016-02-16 Update
最新的swift中,对UInt64
和UInt32
,已经自带了成员方法:public var bigEndian: UInt32 { get }
public var littleEndian: UInt32 { get }
等等. 也就是说,不需要使用CFSwapInt32BigToHost(val)
这样转换了,直接val.bigEndian
即可.
基础数据与NSData的转换
为了能让Swift和JAVA进行网络的交互,那么就必须把它们的基础数据转换成为Bytes字节数组.
在Swift中使用NSData
或者NSMutableData
来表示.因此,也就是需要把基础数据放入到NSData中.
Int32与Int64
在Swift中,Int
是一个特殊的整数类型,它的长度与当前平台的原生字长相同:
- 在32位平台上,
Int
与Int32
长度相同 - 在64位平台上,
Int
与Int64
长度相同.相当于C中的Long.
因此,在网络传输的时候,是需要区分的对待Int32和Int64的.需要把一个Int
类型强转为需要的长度.
具体的代码为:
|
|
这里使用了扩展机制,直接在NSMutableData
上增加扩展.
首先,使用CFSwapInt32HostToBig
函数把字节序给改变了.然后调用NSMutableData.appendBytes
方法,赋值给NSData
.由于NSMutableData
现在还是Objective-C
的实现,因此,调用方式稍微有点奇怪,是使用指针的方式进行赋值的.更多的可以参见Using Swift with Cocoa and Objective-C.
反序列化也是相似的,首先定义了一个变量.然后使用NSMutableData.getBytes
,同样传入一个指针以及数据的返回.然后最后在进行一次字节序的转换即可.
2016-02-16 Update:
对于需要在网络传输中传输负数的情况需要先把负数的Int
转换为无符号的整数UInt
.在计算机中,负数的表示方法是采用补码的形式.在swift中,可以使用UInt32(bitPattern:Int32)
以及Int32(bitPattern:UInt32)
方法来相互的转换.比如,-5
转换为无符号的补码形式为:fffffffb
. 因此我们的appendInt
和getInt
可以改成这样:
|
|
Float32与Float64
它的情况与Int
的非常相似.同样需要经历字节序的转换,以及NSMutableData.appendBytes
的调用.
|
|
Bool
Bool
类型由于是单字节的数据,不存在字节序的问题.因此,它与NSData的转换最为简单.
|
|
String
字符串的处理又相对的要麻烦些了.因为字符串的长度是可变的.不像其他的数据类型是有固定的长度的.因此,一般在网络传输中,都会在字符串的bytes前接上一个Int32
的字节数组来表示这个字符串的长度.
因此,我们在转换String
到NSData
的时候,实际上是两个步骤.首先计算出字符串的字节长度.然后把这个字节长度放入NSData中,接着,再把字符串的内容转换为字节数组放入NSData中:
|
|
总结
以上就是简单的介绍了在Swift中如何把几种常用的数据类型转换为网络交互格式的Bytes数组的.至于其他的数据类型,或者自定义的数据结构,无外乎都是从这几种基础数据类型上拼接出来的,稍微灵活修改下即可.