0Pricing
Swift Academy · 课时

不安全缓冲区指针

高效处理连续内存。

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

缓冲区指针是什么

单个类型化指针一次只能定位一个元素。UnsafeBufferPointer<T> 将起始指针和计数封装在一起,为符合 Collection 的连续内存块提供有边界的视图。

这意味着您可以遍历、使用下标访问、映射和归约——所有这些都是针对原始内存的熟悉集合接口。

// A buffer pointer = base address + element count
// UnsafeBufferPointer<Int>(start: ptr, count: 5)

只读缓冲区与可变缓冲区

与单个指针一样,缓冲区也有两种形式:用于只读遍历的 UnsafeBufferPointer<T>,以及在还需要为元素赋值时使用的 UnsafeMutableBufferPointer<T>。

let p = UnsafeMutablePointer<Int>.allocate(capacity: 4)
p.initialize(repeating: 0, count: 4)
let buf = UnsafeMutableBufferPointer(start: p, count: 4)
buf[0] = 11
print(buf[0]) // 11

像集合一样遍历

由于缓冲区指针是 Collection,for 循环会按顺序遍历每个元素。无需手动进行索引运算,也不会在循环本身中出现差一错误——不过,您提供的边界仍由您负责。

let buf = UnsafeMutableBufferPointer<Int>.allocate(capacity: 3)
_ = buf.initialize(from: [10, 20, 30])
for value in buf { print(value) } // 10, 20, 30

直接使用集合算法

map、filter、reduce、enumerated 和 indices 都可以用于缓冲区指针。这样,您就能使用高级代码处理原始内存,同时无需分配内存。

let buf = UnsafeMutableBufferPointer<Int>.allocate(capacity: 4)
_ = buf.initialize(from: [1, 2, 3, 4])
let total = buf.reduce(0, +)   // 10
let evens = buf.filter { $0 % 2 == 0 } // [2, 4]
print(total, evens)

分配可变缓冲区

UnsafeMutableBufferPointer<T>.allocate(capacity:) 会预留内存块,并在一步操作中返回缓冲区。您仍然必须先执行 initialize,再读取数据,并在完成后执行 deallocate。

let buf = UnsafeMutableBufferPointer<Int>.allocate(capacity: 5)
buf.initialize(repeating: 0)
defer {
    buf.deinitialize()
    buf.deallocate()
}

边界不会在末端自动检查

在 0..<count 内使用下标访问没有问题。但缓冲区无法知道您提供的计数是否正确。如果您构造缓冲区时使用的计数大于实际分配大小,读取就会越过末端,进入未定义的内存。

始终从您创建的同一块内存分配中推导计数。

let p = UnsafeMutablePointer<Int>.allocate(capacity: 3)
p.initialize(repeating: 0, count: 3)
// CORRECT: count matches capacity
let good = UnsafeBufferPointer(start: p, count: 3)
// WRONG: count: 10 would read past the block
print(good.count)

空缓冲区

count: 0 的缓冲区是有效的,并且其基地址可能是 nil。请始终检查 isEmpty,或者依靠循环自然地不执行,而不要强制解包 baseAddress。

let empty = UnsafeBufferPointer<Int>(start: nil, count: 0)
print(empty.isEmpty)       // true
print(empty.count)         // 0
for _ in empty { print("never runs") }

数组上的 withUnsafeBufferPointer

获取缓冲区最安全的方式,是通过 withUnsafeBufferPointer 从现有的 Array 获取。Swift 保证存储空间是连续的,并且在闭包持续期间有效,因此您完全无需手动分配内存。

let numbers = [4, 8, 15, 16, 23]
let maxValue = numbers.withUnsafeBufferPointer { buf -> Int in
    var m = buf[0]
    for v in buf where v > m { m = v }
    return m
}
print(maxValue) // 23

修改数组存储空间

withUnsafeMutableBufferPointer 允许您直接写入数组的存储空间。在对性能要求极高的原地转换中,有时会使用这种方式,因为 ARC 和边界检查会带来额外开销。

var data = [1, 2, 3, 4]
data.withUnsafeMutableBufferPointer { buf in
    for i in buf.indices { buf[i] *= 10 }
}
print(data) // [10, 20, 30, 40]

用于 C 接口的 baseAddress

调用需要指针和长度的 C 函数时,请传入 buffer.baseAddress 和 buffer.count。只有空缓冲区的基地址才是 nil,因此请处理这种情况。

let bytes: [UInt8] = [0xDE, 0xAD, 0xBE, 0xEF]
bytes.withUnsafeBufferPointer { buf in
    if let base = buf.baseAddress {
        // c_function(base, buf.count)
        print("start:", base, "len:", buf.count)
    }
}

何时使用缓冲区指针

当您需要对一段内存进行连续、类型安全且类似集合的访问时,请使用缓冲区指针,例如桥接 C 数组、解析二进制格式,或减少高频循环中的开销。只要可能,请优先使用 with... 闭包形式,而不是手动分配内存。

// Counting set bits across a byte buffer
let data: [UInt8] = [0b1011, 0b0110, 0b1111]
let bits = data.withUnsafeBufferPointer { buf in
    buf.reduce(0) { $0 + $1.nonzeroBitCount }
}
print(bits) // 3 + 2 + 4 = 9

快速检查

回想一下,缓冲区指针相比单个指针增加了什么。

总结

缓冲区指针将连续区域封装为有边界的集合:

  • UnsafeBufferPointer / UnsafeMutableBufferPointer = 基地址 + 计数。
  • 您可以在原始内存上使用 map、filter、reduce,并通过 for 进行遍历。
  • 在数组上优先使用 withUnsafeBufferPointer,让 Swift 管理有效性和生命周期。
  • 提供的计数会被信任——请从实际分配中推导它。

常见问题解答

「不安全缓冲区指针」课时是免费的吗?

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

「不安全缓冲区指针」这节课中我会学到什么?

高效处理连续内存。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

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

「不安全缓冲区指针」课时需要多长时间?

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

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

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

此课程中的所有课时

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