Swift 内存管理简介3

177 阅读2分钟

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

下面我们操作一个结构体来实践一下。

struct ZGTeacher {
    var age: Int
    var height: Double
}

下面我们来尝试看一下如何用指针来访问这个结构体。代码如下:

struct ZGTeacher {
    var age: Int
    var height: Double
}

///capacity: 比如我们需要开辟5个内存空间的大小
///allocate: 开辟空间
var ptr = UnsafeMutablePointer<ZGTeacher>.allocate(capacity: 5)
///初始化我们的指针内存,并存储对应的值
ptr[0] = ZGTeacher(age: 18, height: 20.0)
ptr[1] = ZGTeacher(age: 22, height: 30.0)

///defer关键字,当前程序运行完成后会执行这块代码
defer {
    ///deinitialize回收5个内存空间
    ptr.deinitialize(count: 5)
    ///销毁对应的内存空间
    ptr.deallocate()
}

我们同样还可以如下面代码这样初始化:

struct ZGTeacher {
    var age: Int = 18
    var height: Double = 1.85
}

let p = UnsafeMutablePointer<ZGTeacher>.allocate(capacity: 2)
p.initialize(to: ZGTeacher())
p.advanced(by: MemoryLayout<ZGTeacher>.stride).initialize(to: ZGTeacher(age: 20, height: 1.75))

defer {
    p.deinitialize(count: 2)
    p.deallocate()
}

1.3 内存绑定

swift提供了三种不同的API来绑定/重新绑定指针:

  • assumingMemoryBound(to:)
func testPointer(_ p: UnsafePointer<Int>) {
    print(p)
}

let tuple = (10, 20)
withUnsafePointer(to: tuple) { (tuplePtr: UnsafePointer<(Int, Int)>) in
    testPointer(tuplePtr)
}

编译运行,发现报错了,如下图所示

image.png

那么想要顺利访问到元组的值我们可以怎么办哪? 首先,第一点,我们先把我们的指针转换为原生指针**UnsafeRawPointer,然后我们调用assumingMemoryBound**绑定成对应类型。

func testPointer(_ p: UnsafePointer<Int>) {
    print(p[0], p[1])
}

let tuple = (10, 20)

withUnsafePointer(to: tuple) { (tuplePtr: UnsafePointer<(Int, Int)>) in
    testPointer(UnsafeRawPointer(tuplePtr).assumingMemoryBound(to: Int.self))
}

编译一下,正确输出打印元组的值 1020
那么我们使用assumingMemoryBound的意义是什么?
有时候我们的类型只有这种原生指针UnsafeRawPointer,或者说像UnsafePointer<(Int, Int)>UnsafePointer 这种,两种值类型相似,但是我们又不想经过一系列的转换操作来增加代码复杂度,对我们的指针进行生硬的转换,那么这个时候我们就可以使用这个API assumingMemoryBound来告诉我们的编译器自己预期的类型,不需要编译器再重复检查(注意:这⾥只是让编译器绕过类型检查,并没有发⽣实际类型的转换)。

  • bindMemory(to: , capacity: )
    调用**bindMemory**绑定成对应类型,这里我们发生了实际类型的转换。如果当前的内存没有绑定类型,那么我们就首次绑定类型,如果有它当前的原有类型,那么调用这个API,我们重新绑定为指定类型。
func testPointer(_ p: UnsafePointer<Int>) {
    print(p[0], p[1])
}

let tuple = (10, 20)

withUnsafePointer(to: tuple) { (tuplePtr: UnsafePointer<(Int, Int)>) in
    testPointer(UnsafeRawPointer(tuplePtr).bindMemory(to: Int.self, capacity: 1))
}

这里我们将UnsafePointer<(Int, Int)> 转换成了UnsafePointer 类型。

  • withMemoryRebound(to: , capacity: )
    withMemoryRebound就是用来临时更改我们的类型,减少代码复杂度。
func testPoint(_ p: UnsafePointer<Int8>) {

}

let UInt8Ptr = UnsafePointer<UInt8>.init(bitPattern: 10)
UInt8Ptr?.withMemoryRebound(to: Int8.self, capacity: 1, { (int8Ptr: UnsafePointer<Int8>) in
    testPoint(int8Ptr)
})