Swift 内存管理简介2

158 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情

为了便于理解**MemoryLayout**,我们先来看下下面这个例子:

struct ZGTeacher {
    var age: Int = 18
}

let size = MemoryLayout<ZGTeacher>.size
let stride = MemoryLayout<ZGTeacher>.stride
let alignment = MemoryLayout<ZGTeacher>.alignment

print(size, stride, alignment)

lldb打印结果

8 8 8

我们给ZGTeacher结构体新增一个Bool属性看一下它的变化。

struct ZGTeacher {
    var age: Int = 18
///新增一个Bool属性
    var sex: Bool = true
}
省略......

lldb打印结果如下:

9 16 8

其中 alignment 是不变的,但是 sizestride 都变了。

MemoryLayout是Swift标准库中定义的一个枚举,顾名思义其是用于获取内存相关信息,MemoryLayout<Int> 则是一种泛型的用法,调用其**size属性可以获取某种数据类型所占内存空间的字节数,是其在内存中真实占用大小。调用其stride属性可以获取某种数据类型所开辟内存空间的字节数,是系统为其分配的内存大小。调用其alignment**属性可以获取某种数据类型所需要的对齐信息,指的是其当前内存对齐方式,是1字节对齐,4字节对齐等。

这⾥我们回到我们的指针操作,此时我们应该明⽩我们在存储 4 个连续整型的数据时候的问题了,那么 就是我们并没有指定当前 Int 数据在排列过程中每个数据和每个数据之间的间隔是多少?
代码修改如下:

///advanced 移动对应的步长,来存放内容
let size = MemoryLayout<Int>.size
let stride = MemoryLayout<Int>.stride
let alignment = MemoryLayout<Int>.alignment

let p = UnsafeMutableRawPointer.allocate(byteCount: 4 * stride, alignment: alignment)

for i in 0 ..< 4 {
    p.advanced(by: i * stride).storeBytes(of: i, as: Int.self)
///(p + i * stride).storeBytes(of: i, as: Int.self)
}

print(p)

for i in 0 ..< 4 {
    let value = p.load(fromByteOffset: i * 8, as: Int.self)
    print("index: (i), value: (value)")
}
p.deallocate()

lldb打印结果:

0x0000000105058e30
index: 0, value: 0
index: 1, value: 1
index: 2, value: 2
index: 3, value: 3

这才是正确的,符合我们期望的存储内容打印。
其中

p.advanced(by: i * stride).storeBytes(of: i, as: Int.self)

也等价于

///p,基地址,i *stride,移动对应的步长位置来存放i
(p + i * stride).storeBytes(of: i, as: Int.self)

1.2.2 泛型指针的使用

这⾥的泛型指针相⽐较原⽣指针来说,其实就是指定当前指针已经绑定到了具体的类型。同样的,我们 还是通过⼀个例⼦来解释⼀下。

var age = 18
withUnsafePointer(to: &age) { ptr in
    print(ptr)
}

lldb输出打印

0x0000000100008058

我们得到了age这个变量的内存指针。那么如果我们想要修改age这个变量可以怎么办哪?
在进⾏泛型指针访问的过程中,我们并不是使⽤ loadstore ⽅法来进⾏存储操作。这⾥我们使⽤到当前泛型指针内置的变量 pointee。 获取 UnsafePointer 的⽅式有两种。
⼀种⽅式就是通过已有变量获取,如下:

var age = 18
withUnsafePointer(to: &age) { ptr in
    print(ptr)
}

age = withUnsafePointer(to: age) { ptr in
    return ptr.pointee + 20
}
print(age)

发现更改后,它的存储地址和存储的值已经发生改变。

0x0000000100008068
38

注意一点,这里我们无法直接修改**ptr.pointee,如果想要修改,我们可以通过可变类型的MutablePointer**,代码如下:

var age = 18

withUnsafePointer(to: &age) { ptr in
    print(ptr)
}

withUnsafeMutablePointer(to: &age) { ptr in
    ptr.pointee += 50
}
print(age)

lldb输出打印结果:

x0000000100008068
68

还有一种方式就是直接分配内存

var age = 18
///1、分配一块Int类型的内存空间,注意这个时候当前内存空间还没有被初始化
let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
///2、为age initialize初始化分配的内存空间
ptr.initialize(to: age)
///3、访问当前内存的值,直接通过pointee属性来进行访问
print(ptr.pointee)