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 elementInitialize 与 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 反馈 — 无需本地设置。
此课程中的所有课时
- MemoryLayout 与对齐
- UnsafePointer 与 UnsafeMutablePointer
- 不安全缓冲区指针
- withUnsafeBytes 与 C 互操作