Указатели на небезопасные буферы
Эффективно обрабатывайте непрерывную область памяти.
«Указатели на небезопасные буферы» — бесплатный урок Swift Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
Что такое указатель буфера
Указатель с одним типом адресует только один элемент за раз. UnsafeBufferPointer<T> оборачивает указатель на начало и количество элементов, предоставляя ограниченное представление непрерывного блока памяти, соответствующее Collection.
Это означает, что Вы можете перебирать элементы, обращаться к ним по индексу, преобразовывать их и применять reduce — все знакомые интерфейсы коллекций для работы с необработанной памятью.
// 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 для массивов
Безопаснее всего получить буфер из уже существующего Array с помощью withUnsafeBufferPointer. 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]baseAddress для API C
При вызове функции 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 управлял корректностью и временем жизни данных. - Указанное количество элементов считается достоверным — выводите его из фактически выделенной памяти.
Часто задаваемые вопросы
Урок «Указатели на небезопасные буферы» бесплатный?
Да — полный текст урока «Указатели на небезопасные буферы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «Указатели на небезопасные буферы»?
Эффективно обрабатывайте непрерывную область памяти. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Указатели на небезопасные буферы»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- MemoryLayout и выравнивание
- UnsafePointer и UnsafeMutablePointer
- Указатели на небезопасные буферы
- withUnsafeBytes и взаимодействие с C