0Pricing
Swift Academy · 课时

UnsafePointer 与 UnsafeMutablePointer

谨慎地读写原始内存。

UnsafePointer 与 UnsafeMutablePointer 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。

Swift 中的类型化指针

Swift 的安全引用不会暴露 raw 地址,但在与 C 互操作或构建底层数据结构时,您会使用类型化指针:UnsafePointer<T> 用于只读访问,UnsafeMutablePointer<T> 用于读写访问。

类型化指针了解元素类型,因此可以计算 stride 并读取类型正确的值。

// A pointer to an Int we can read but not write
func readFirst(_ p: UnsafePointer<Int>) -> Int {
    return p.pointee
}

pointee 属性

pointee 是指针当前指向的值。在可变指针上,您既可以读取也可以为其赋值;在只读指针上,您只能读取。

访问未初始化或已 deallocate 的内存中的 pointee 会产生未定义行为——类型系统无法捕获这种错误。

let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
p.initialize(to: 42)
print(p.pointee)   // 42
p.pointee = 99
print(p.pointee)   // 99

分配内存

UnsafeMutablePointer<T>.allocate(capacity:) 会为 capacity 个元素预留空间,但不会初始化它们。这些内存是 raw 的,在初始化前读取属于非法操作。

// Reserve space for 3 Doubles (uninitialized)
let buffer = UnsafeMutablePointer<Double>.allocate(capacity: 3)
// Must initialize before reading any element

Initialize 与 Deinitialize

读取之前必须执行 initialize。释放需要清理的类型(例如类或字符串)之前,必须执行 deinitialize 来释放其资源。

生命周期为:allocate → initialize → 使用 → deinitialize → deallocate。

let p = UnsafeMutablePointer<String>.allocate(capacity: 1)
p.initialize(to: "hello")
print(p.pointee)
p.deinitialize(count: 1) // releases the String
p.deallocate()

始终将 allocate 与 deallocate 配对

每次 allocate 都必须恰好对应一次 deallocate,否则就会发生内存泄漏。在分配后立即使用 defer 是最安全的模式,因为无论函数如何退出,它都会执行。

func process() {
    let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    defer { p.deallocate() }
    p.initialize(to: 7)
    print(p.pointee)
} // deallocate runs here automatically

指针运算

类型化指针支持以元素单位进行索引和运算,而不是以字节为单位。p + 2 会前进两个 T 的 stride。p[i] 是 (p + i).pointee 的简写。

let p = UnsafeMutablePointer<Int>.allocate(capacity: 3)
p.initialize(repeating: 0, count: 3)
p[0] = 10
p[1] = 20
p[2] = 30
print((p + 1).pointee) // 20
p.deinitialize(count: 3)
p.deallocate()

初始化多个元素

对于一块内存,initialize(repeating:count:) 会将每个位置设为相同的值,而 initialize(from:count:) 会从另一个缓冲区复制。两者都会完整初始化整个范围,因此可以安全读取。

let dst = UnsafeMutablePointer<Int>.allocate(capacity: 4)
dst.initialize(repeating: 5, count: 4)
print(dst[3]) // 5
dst.deinitialize(count: 4)
dst.deallocate()

只读与可变

只检查数据的函数应接收 UnsafePointer<T>;修改数据的函数应接收 UnsafeMutablePointer<T>。可变指针可以隐式转换为只读指针,但反过来不行——这会在接口边界上确保调用意图明确。

func sum(_ p: UnsafePointer<Int>, _ n: Int) -> Int {
    var total = 0
    for i in 0..<n { total += p[i] }
    return total
}
// A mutable pointer can be passed here directly

使用 withUnsafePointer 处理局部值

若要在不分配内存的情况下获取现有值的临时指针,请使用 withUnsafePointer(to:) 或其可变形式。该指针只在闭包内部有效——绝不能让它逃逸。

var value = 100
withUnsafeMutablePointer(to: &value) { ptr in
    ptr.pointee += 1
}
print(value) // 101

常见陷阱

类型化指针功能强大,但容错性很低:

  • 在 initialize 之前读取 pointee 会产生未定义行为。
  • 对于持有资源的类型,忘记执行 deinitialize 会造成泄漏。
  • 让 withUnsafePointer 指针逃逸会产生悬空指针。

在这里,编译器会信任您——运行时没有安全防护措施。

// DANGEROUS — pointer escapes the closure
// var escaped: UnsafePointer<Int>?
// withUnsafePointer(to: &x) { escaped = $0 }
// escaped!.pointee  // dangling, undefined behavior

一个小型手动 stack

将这些内容结合起来:分配一个内存块,将其作为可索引的元素使用,然后进行清理。这是自定义集合和 C 互操作的基础。

let n = 3
let stack = UnsafeMutablePointer<Int>.allocate(capacity: n)
stack.initialize(repeating: 0, count: n)
for i in 0..<n { stack[i] = i * i }
// stack now holds 0, 1, 4
stack.deinitialize(count: n)
stack.deallocate()

快速检查

回想正确的指针生命周期。

回顾

现在您已经了解了类型化指针:

  • UnsafePointer 用于读取,UnsafeMutablePointer 用于读取和写入。
  • pointee 用于访问值;运算以元素为单位。
  • 生命周期为 allocate → initialize → 使用 → deinitialize → deallocate,最好使用 defer 进行保护。

接下来,您将了解缓冲区指针如何在便于集合使用的 API 中包装整个连续区域。

常见问题解答

「UnsafePointer 与 UnsafeMutablePointer」课时是免费的吗?

是的 — 「UnsafePointer 与 UnsafeMutablePointer」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。

「UnsafePointer 与 UnsafeMutablePointer」这节课中我会学到什么?

谨慎地读写原始内存。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「UnsafePointer 与 UnsafeMutablePointer」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Swift Academy 课中编写并运行代码吗?

能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. MemoryLayout 与对齐
  2. UnsafePointer 与 UnsafeMutablePointer
  3. 不安全缓冲区指针
  4. withUnsafeBytes 与 C 互操作
← 返回 Swift Academy