0Pricing
Swift Academy · Урок

Указатели на небезопасные буферы

Эффективно обрабатывайте непрерывную область памяти.

«Указатели на небезопасные буферы» — бесплатный урок 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 — локальная установка не требуется.

Все уроки этого курса

  1. MemoryLayout и выравнивание
  2. UnsafePointer и UnsafeMutablePointer
  3. Указатели на небезопасные буферы
  4. withUnsafeBytes и взаимодействие с C
← Назад к Swift Academy