文档章节

Swift中基本数据类型与NSData转换

hejunbinlan
 hejunbinlan
发布于 2016/07/25 15:07
字数 2284
阅读 1102
收藏 0
  1. 1. Swift中基本数据类型与NSData转换
    1. 1.1. big-endian and little-endian
    2. 1.2. 基础数据与NSData的转换
      1. 1.2.1. Int32与Int64
      2. 1.2.2. Float32与Float64
      3. 1.2.3. Bool
      4. 1.2.4. String
    3. 1.3. 总结

Swift中基本数据类型与NSData转换

最近由于程序的需要,要与JAVA的服务端进行Socket的交互,那么这就牵涉到了数据的交互.Socket的数据交互一般都是直接采用二进制Bytes的方式来传递,那么就需要把Swift中的各种基本数据转换成为JAVA服务器可以认可的Bytes字节数组,以及把JAVA的字节数组反序列化为Swift中的基本数据.

big-endian and little-endian

要在不同程序中进行字节数组的数据交换,有个很重要的东西就是字节序.字节序顾名思义就是字节的顺序,也就是大于1个字节类型的数据在内存中存放的顺序.这个在跨平台以及网络程序交互中非常的重要.

常见的字节序主要有两类:Big-EndianLittle-Endian.它们的定义为:

  • Big-Endian:高位字节排放在内存的低地址端,地位字节排放在内存的高地址端.
  • Little-Endian:地位字节排放在内存的低地址端,高位字节排放在内存的高地址端.
    这样说可能比较抽象,我们来举个例子就非常清楚了:
    比如一个32位的Int类型数据: let a = Int32(2)分别采用Big-EndianLittle-Endian的情况如下:
字节号 0 1 2 3
Big-endian 00 00 00 02
Little-Endian 02 00 00 00

也就是他们两个是相反的.Big-EndianLittle-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上的SwiftJAVA的字节序是相反的,因此,它们两个进行跨语言的网络数据交互的时候,就需要对数据进行字节序的转换.否则就会出现数据读取错误的情况,比如用JAVA采用Big-Endian序的Int3202000000,Swift采用Little-Endian序解析出来是33554432而不是期望的2.

在Swift中,Apple在CoreFoundation中提供了一些列的函数来提供字节序的转换.它们都在CFByteOrder中有所定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public func CFSwapInt16BigToHost(arg: UInt16) -> UInt16

public func CFSwapInt32BigToHost(arg: UInt32) -> UInt32

public func CFSwapInt64BigToHost(arg: UInt64) -> UInt64

public func CFSwapInt16HostToBig(arg: UInt16) -> UInt16

public func CFSwapInt32HostToBig(arg: UInt32) -> UInt32

public func CFSwapInt64HostToBig(arg: UInt64) -> UInt64

public func CFSwapInt16LittleToHost(arg: UInt16) -> UInt16

public func CFSwapInt32LittleToHost(arg: UInt32) -> UInt32

public func CFSwapInt64LittleToHost(arg: UInt64) -> UInt64

public func CFSwapInt16HostToLittle(arg: UInt16) -> UInt16

public func CFSwapInt32HostToLittle(arg: UInt32) -> UInt32

public func CFSwapInt64HostToLittle(arg: UInt64) -> UInt64

public func CFConvertFloat32HostToSwapped(arg: Float32) -> CFSwappedFloat32

public func CFConvertFloat32SwappedToHost(arg: CFSwappedFloat32) -> Float32

public func CFConvertFloat64HostToSwapped(arg: Float64) -> CFSwappedFloat64

public func CFConvertFloat64SwappedToHost(arg: CFSwappedFloat64) -> Float64

public func CFConvertFloatHostToSwapped(arg: Float) -> CFSwappedFloat32

public func CFConvertFloatSwappedToHost(arg: CFSwappedFloat32) -> Float

public func CFConvertDoubleHostToSwapped(arg: Double) -> CFSwappedFloat64

public func CFConvertDoubleSwappedToHost(arg: CFSwappedFloat64) -> Double

使用这些函数就可以对字节序进行转换. 更多的可以参考Apple的官方手册

2016-02-16 Update
最新的swift中,对UInt64UInt32,已经自带了成员方法: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位平台上,IntInt32长度相同
  • 在64位平台上,IntInt64长度相同.相当于C中的Long.

因此,在网络传输的时候,是需要区分的对待Int32和Int64的.需要把一个Int类型强转为需要的长度.
具体的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public extension NSMutableData {
    
    public func appendInt(value:Int){
        var networkOrderVal = CFSwapInt32HostToBig(UInt32(value))
        self.appendBytes(&networkOrderVal, length: sizeof(UInt32))
    }
    
    public func appendLong(value:Int) {
        var networkOrderVal = CFSwapInt64HostToBig(UInt64(value));
        self.appendBytes(&networkOrderVal, length: sizeof(UInt64))
    }
    
    public func getInt(range:NSRange = NSRange(location:0,length:sizeof(UInt32))) -> Int {
        var val: UInt32 = 0
        self.getBytes(&val, range: range)
        return Int(CFSwapInt32BigToHost(val))
    }
    
    public func getLong(range:NSRange = NSRange(location:0,length:sizeof(UInt64))) -> Int {
        var val: UInt64 = 0
        self.getBytes(&val, range: range)
        return Int(CFSwapInt64BigToHost(val))
    }
}

这里使用了扩展机制,直接在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. 因此我们的appendIntgetInt可以改成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public func appendInt(value:Int){
       var networkOrderVal = UInt32(bitPattern:Int32(value)).bigEndian
       self.appendBytes(&networkOrderVal, length: sizeof(UInt32))
   }
   
   public func appendLong(value:Int) {
       var networkOrderVal = UInt64(bitPattern:Int64(value)).bigEndian
       self.appendBytes(&networkOrderVal, length: sizeof(UInt64))
   }
   
   public func getInt(range:NSRange = NSRange(location:0,length:sizeof(UInt32))) -> Int {
       var val: UInt32 = 0
       self.getBytes(&val, range: range)
       return Int(Int32(bitPattern:val.bigEndian))
   }
   
   public func getLong(range:NSRange = NSRange(location:0,length:sizeof(UInt64))) -> Int {
       var val: UInt64 = 0
       self.getBytes(&val, range: range)
       return Int(Int64(bitPattern:val.bigEndian))
   }

Float32与Float64

它的情况与Int的非常相似.同样需要经历字节序的转换,以及NSMutableData.appendBytes的调用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public extension NSMutableData {
    
    // MARK: Float32与Float64
    
    public func appendFloat(value:Float) {
        var networkOrderVal = CFConvertFloat32HostToSwapped(Float32(value))
        self.appendBytes(&networkOrderVal, length: sizeof(Float32))
    }
    
    public func appendDouble(value:Double) {
        var networkOrderVal = CFConvertFloat64HostToSwapped(Float64(value))
        self.appendBytes(&networkOrderVal, length: sizeof(Float64))
    }
    
    
    public func getFloat(range:NSRange = NSRange(location:0,length:sizeof(Float32))) -> Float {
        
        var val: CFSwappedFloat32 = CFSwappedFloat32(v: 0)
        self.getBytes(&val, range: range)
        let result = CFConvertFloat32SwappedToHost(val)
        return result
    }
    
    public func getDouble(range:NSRange = NSRange(location:0,length:sizeof(Float64))) -> Double {
        var val: CFSwappedFloat64 = CFSwappedFloat64(v: 0)
        self.getBytes(&val, range: range)
        let result = CFConvertFloat64SwappedToHost(val)
        return result
    }
}

Bool

Bool类型由于是单字节的数据,不存在字节序的问题.因此,它与NSData的转换最为简单.

1
2
3
4
5
6
7
8
9
10
11
12
public extension NSMutableData {
	// MARK: Bool
    public func appendBool(var val:Bool){
        self.appendBytes(&val, length: sizeof(Bool))
    }
    
    public func getBool(range:NSRange = NSRange(location:0,length:sizeof(Bool))) -> Bool {
        var val:Bool = false
        self.getBytes(&val, range: range)
        return val
    }
}

String

字符串的处理又相对的要麻烦些了.因为字符串的长度是可变的.不像其他的数据类型是有固定的长度的.因此,一般在网络传输中,都会在字符串的bytes前接上一个Int32的字节数组来表示这个字符串的长度.

因此,我们在转换StringNSData的时候,实际上是两个步骤.首先计算出字符串的字节长度.然后把这个字节长度放入NSData中,接着,再把字符串的内容转换为字节数组放入NSData中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public extension NSMutableData {
	// MARK: String
    public func appendString(val:String,encoding:NSStringEncoding = NSUTF8StringEncoding){
        
        //获取到字节的长度,使用某一种编码
        let pLength : Int = val.lengthOfBytesUsingEncoding(encoding)

        //放入字符串的长度
        self.appendInt(pLength)
        
        //把字符串按照某种编码转化为字节数组
        let data = val.dataUsingEncoding(encoding)
        
        //放入NSData中
        self.appendData(data!)
    }

    public func getString(location:Int = 0,encoding:NSStringEncoding = NSUTF8StringEncoding) throws -> (String,Int){
        
        //先获取到长度
        let len = self.getInt(NSRange(location:location,length:sizeof(UInt32)))
        
        //找到子字节数组
        let subData = self.subdataWithRange(NSRange(location: location+sizeof(UInt32), length: len))
        
        //直接使用String的构造函数,采用某种编码格式获取字符串
        let string = String(data: subData, encoding: encoding)
        
        //如果凑不起字符串,就表示数据不正确,那么就抛出异常
        guard let _string = string else {
            throw AppException.FormatCastException
        }
        
        //返回结果
        return (_string,len+sizeof(UInt32))
    }
}

总结

以上就是简单的介绍了在Swift中如何把几种常用的数据类型转换为网络交互格式的Bytes数组的.至于其他的数据类型,或者自定义的数据结构,无外乎都是从这几种基础数据类型上拼接出来的,稍微灵活修改下即可.

本文转载自:http://sunxiang0918.cn/2016/02/15/Swift%E4%B8%AD%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%...

共有 人打赏支持
hejunbinlan
粉丝 41
博文 586
码字总数 21569
作品 0
浦东
高级程序员
Swift3.0语言教程删除字符与处理字符编码

Swift3.0语言教程删除字符与处理字符编码 Swift3.0语言教程删除字符 Swift3.0语言教程删除字符与处理字符编码,在字符串中,如果开发者有不需要使用的字符,就可以将这些字符删除。在NSStrin...

大学霸
2016/11/17
86
1
IOS(swift)-数据存储 · NSKeyedArchiver 归档

需求: 有一个通讯列表,可以自行添加联系数据,但是重新开启后,添加的数据都会清空,我希望打开后,上一次的数据能保留。 这我们就必须用到数据持久化,这一次,我将用NSKeyedArchiver 归档...

SoulJa
2015/10/31
0
0
NSData与NSString

NSdata的概念 1、使用文件时需要频繁地将数据读入一个临时存储区,它通常称为缓冲区 2、NSdata类提供了一种简单的方式,它用来设置缓冲区,将文件的内容读入缓冲区,或者将缓冲区内容写到一个...

yoyoso
2014/12/23
229
0
Swift-使用NSJSONSerialization生成或解析JSON数据

NSJSONSerialization是iOS5中增加的解析JSON的API. NSJSONSerialization提供了将JSON数据转换为Foundation对象(一般都是NSDictionary和NSArray)和 Foundation对象转换为JSON数据. 在将Fou...

王永濤
2015/10/27
0
0
从 OC 到 Swift 的快速入门与专业实践

只会介绍与 OC 有明显区别的地方,不会介绍 OC 中没有的,比如元组。当前总结也只是蜻蜓点水而已,但是有 OC 的基础,看这些已经足够。 一、数据 Swift 是类型安全的语言: Swift 必须明确数...

CoderHG
08/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

macOs-挂载能读写的NTFS硬盘

转自:https://nicklinyi.gitee.io/blog/2018/04/macOS-ntfs.html Mac本身是支持NTFS写入的,只是NTFS是微软开发,由于版权和技术细节原因,苹果不愿公开说自己支持NTFS写入,也是有自己以后...

北风刮的不认真了
3分钟前
0
0
Namespace 命名空间

命名空间可以定义为一种封装方式。 为了解决开发中库和程序中可重用类和方法问题: 1.解决 PHP内部方法类/方法/常量 或者第三方 类/方法/常量之间的命名冲突 2.能够简化为了防止命名冲突而给...

忙碌的小蜜蜂
5分钟前
0
0
CDH的坑之Deploy Client Configuration Failed

Deploy Client Configuration Failed 1.问题描述 当使用CDH增添spark服务的时候,出现了以下错误: Faile to deploy client configuration to the cluster. 具体如下图: 2.思路 网上查了...

星汉
6分钟前
0
0
java guava 集合的操作:交集、差集、并集

Guava:google的工程师利用传说中的“20%时间”开发的集合库,它是对jdk提供的扩展,提供了很多实用的类来简化代码。 开源地址:https://github.com/google/guava jar包下载:http://maven....

帅的不像男的
7分钟前
0
0
从八个层面比较分析 Java 8, RxJava, Reactor

响应式编程在单机环境下是否鸡肋? 结论是:没有结论,我觉得只能抱着怀疑的眼光审视这个问题了。另外还聊到了 RSocket 这个最近在 SpringOne 大会上比较火爆的响应式”新“网络协议,githu...

小刀爱编程
10分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部