一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
一、指针
1.1 为什么说指针不安全
- ⽐如我们在创建⼀个对象的时候,是需要在堆分配内存空间的。但是这个内存空间的生命周期是有限的,也就意味着如果我们使⽤指针指向这块内存空间,如果当前内存空间的⽣命周期已经到了(引⽤计数为0),那么我们当前的指针是不是就变成了未定义的行为了。
- 我们创建的内存空间是有边界的,⽐如我们创建⼀个⼤⼩为**
10**的数组,这个时候我们通过指针访问 到了 **index = 11**的位置,这个时候数组是不是就越界了,访问了⼀个未知的内存空间。 - 指针类型与内存的值类型不⼀致,也是不安全的。
1.2 指针类型
Swift中的指针分为两类, typed pointer 指定数据类型指针, raw pointer 未指定数据类型的指针(原⽣指针)。基本上我们接触到的指针类型有以下⼏种:
1.2.1 原生指针的使用
我们⼀起来看⼀下如何使⽤ Raw Pointer 来存储 4 个整型的数据,这⾥我们需要选取的是 UnsafeMutableRawPointer
///1、开辟一块内存空间
/// UnsafeMutableRawPointer存储原生指针
/// allocate:开辟空间
///byteCount: 当前总的内存大小 4个Int整型,一个整型是8字节,共32字节
///alignment: 对齐的大小
///2、调用storeBytes方法存储当前的整型数值
///of: 存储值
///as: 值的类型,当前类型是整型
///3、我们打印输出验证一下
///调用load方法加载当前内存当中的数据
///fromByteOffset: 距离首地址的字节的大小,每次移动i * 8字节
///as: 值的类型,当前类型是整型
///4、收回并释放对应的内存空间
/// deallocate
let p = UnsafeMutableRawPointer.allocate(byteCount: 4 * 8, alignment: 8)
for i in 0 ..< 4 {
p.storeBytes(of: i, as: Int.self)
}
for i in 0 ..< 4 {
let value = p.load(fromByteOffset: i * 8, as: Int.self)
print("index: (i), value: (value)")
}
p.deallocate()
编译运行,我们调用打印数据查看一下
index: 0, value: 3
index: 1, value: 0
index: 2, value: 16
index: 3, value: 0
结果发现,这个打印结果并不能和我们存储的数据一致。那么问题出在哪里哪?
我们在存储后打印一下p的内存地址
省略......
for i in 0 ..< 4 {
p.storeBytes(of: i, as: Int.self)
}
print(p)
lldb打印结果如下:
0x000000010073c4e0
(lldb) x/8g 0x000000010073c4e0
0x10073c4e0: 0x0000000000000003 0x0000000000000000
0x10073c4f0: 0x0000000000000010 0x0000000000000000
0x10073c500: 0x000000004d55545a 0x000020a000000000
0x10073c510: 0x4d55545a00000000 0x0000000000000000
这⾥很显然是我们对当前数据的存储⽅式不对,按道理来说我们是 8 个字节, 8个字节的排列开来,⽽ 在这个过程中存储的好像不知道每个数据与数据之间的间距,所以这⾥我们需要指定⼀个东⻄,那就是每个数据之间在内存中的间距。
经探究发现,是因为我们存储时storeBytes并没有移动对应的步长信息导致的。那么什么是步长信息哪,我们首先要了解一个枚举类型**MemoryLayout**。