withUnsafeBytes и взаимодействие с C
Подключайтесь к API C, ожидающим необработанные байты.
«withUnsafeBytes и взаимодействие с C» — бесплатный урок Swift Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
От типизированных данных к необработанным байтам
Иногда значение нужно рассматривать не как Int или struct, а как последовательность необработанных байтов — чтобы вычислить его хеш, записать в файл или передать API C. withUnsafeBytes предоставляет именно это: временное представление хранилища значения в виде UInt8.
var value: UInt32 = 0x01020304
withUnsafeBytes(of: &value) { rawBuffer in
print(rawBuffer.count) // 4 bytes
}UnsafeRawBufferPointer
Замыкание получает UnsafeRawBufferPointer — буфер байтов без типа элементов. Вы обращаетесь к нему по индексу, чтобы прочитать отдельные значения UInt8. Он доступен только для чтения; изменяемая форма — withUnsafeMutableBytes.
var value: UInt32 = 0x01020304
withUnsafeBytes(of: &value) { bytes in
for b in bytes { print(String(format: "%02x", b)) }
}
// Order depends on endiannessПорядок байтов имеет значение
Наблюдаемый порядок байтов зависит от порядка байтов платформы. На машинах с младшим порядком байтов (Apple silicon, Intel) первым идёт младший байт. При сериализации для протокола явно преобразуйте порядок с помощью bigEndian или littleEndian.
let host: UInt32 = 0x01020304
let networkOrder = host.bigEndian
var be = networkOrder
withUnsafeBytes(of: &be) { bytes in
// Always 01 02 03 04 regardless of platform
print(Array(bytes))
}Байты структуры
withUnsafeBytes(of:) работает с любым значением, включая структуры. Длина буфера равна MemoryLayout<T>.size, а все байты-заполнители включаются в него, хотя их содержимое не определено.
struct RGB { var r: UInt8; var g: UInt8; var b: UInt8 }
var color = RGB(r: 255, g: 128, b: 0)
withUnsafeBytes(of: &color) { bytes in
print(bytes[0], bytes[1], bytes[2]) // 255 128 0
}Загрузка значения из байтов
Чтобы интерпретировать необработанные байты как типизированное значение, используйте load(fromByteOffset:as:) для необработанного буфера. Смещение должно соответствовать выравниванию типа, а байты действительно должны представлять допустимое значение этого типа.
let raw: [UInt8] = [0x04, 0x03, 0x02, 0x01]
let n = raw.withUnsafeBytes { ptr in
ptr.load(fromByteOffset: 0, as: UInt32.self)
}
print(String(format: "0x%08x", n))loadUnaligned для упакованных данных
Двоичные форматы часто упаковывают поля без выравнивающих заполнителей. Вызов load по невыровненному смещению является неопределённым поведением; вместо него используйте loadUnaligned(fromByteOffset:as:), чтобы безопасно читать данные по любому смещению.
let packet: [UInt8] = [0xFF, 0x04, 0x03, 0x02, 0x01]
let field = packet.withUnsafeBytes { ptr in
ptr.loadUnaligned(fromByteOffset: 1, as: UInt32.self)
}
print(field)Передача Data из Swift в C
Многие функции C принимают const void * и длину. Data и массивы предоставляют withUnsafeBytes, поэтому можно напрямую передать baseAddress и count без копирования.
import Foundation
let data = Data([0x68, 0x69]) // "hi"
data.withUnsafeBytes { (raw: UnsafeRawBufferPointer) in
if let base = raw.baseAddress {
// c_consume(base, raw.count)
print("ptr", base, "len", raw.count)
}
}Получение указателя C
Когда обратный вызов C передаёт Вам const void * и длину, оберните их в UnsafeRawBufferPointer, чтобы безопасно перебирать данные. Swift импортирует указатель C как UnsafeRawPointer?.
func handleC(_ ptr: UnsafeRawPointer, _ len: Int) {
let buffer = UnsafeRawBufferPointer(start: ptr, count: len)
let checksum = buffer.reduce(0) { $0 &+ Int($1) }
print("checksum:", checksum)
}bindMemory и переинтерпретация
Чтобы рассматривать необработанную область как типизированные элементы, bindMemory(to:capacity:) сообщает компилятору, как её интерпретировать. Неправильное использование этой операции (привязка одной и той же памяти к двум несовместимым типам) нарушает строгие правила псевдонимирования Swift и является неопределённым поведением.
let raw = UnsafeMutableRawPointer.allocate(
byteCount: 8, alignment: 8)
defer { raw.deallocate() }
let typed = raw.bindMemory(to: Int.self, capacity: 1)
typed.pointee = 1234
print(typed.pointee)assumingMemoryBound и bindMemory
bindMemory навсегда изменяет способ интерпретации памяти, а assumingMemoryBound(to:) утверждает, что память уже привязана к этому типу, ничего не изменяя. Используйте последний вариант только при полной уверенности в существующей привязке, например для памяти, которая, как Вам известно, содержит значения указанного типа.
func sumInts(_ raw: UnsafeRawPointer, _ count: Int) -> Int {
let p = raw.assumingMemoryBound(to: Int.self)
var total = 0
for i in 0..<count { total += p[i] }
return total
}Проверка безопасности
Именно при работе с необработанными байтами и взаимодействии с C чаще всего возникают сбои. Помните:
- Никогда не позволяйте указателю из замыкания выйти за пределы этого замыкания.
- Учитывайте выравнивание или используйте варианты
Unaligned. - Явно обрабатывайте
endiannessпри сериализации. - Не привязывайте память заново к конфликтующим типам.
- Для пустых буферов проверяйте, не равен ли
baseAddressзначениюnil.
// Convert any value to a hex string safely
import Foundation
func hex<T>(_ v: T) -> String {
var value = v
return withUnsafeBytes(of: &value) { bytes in
bytes.map { String(format: "%02x", $0) }.joined()
}
}Быстрая проверка
Проверьте, насколько хорошо Вы научились читать упакованные двоичные данные.
Итоги
Вы научились связывать значения Swift и C с помощью необработанных байтов:
withUnsafeBytes(of:)представляет любое значение какUInt8.load/loadUnalignedинтерпретируют байты как типизированные значения с учётом выравнивания.Dataи массивы предоставляютwithUnsafeBytes, чтобы передавать в C указатель и длину.bindMemoryиassumingMemoryBoundуправляют типизацией необработанной памяти.- Порядок байтов, выравнивание и выход указателя за пределы замыкания — главные источники опасности.
Часто задаваемые вопросы
Урок «withUnsafeBytes и взаимодействие с C» бесплатный?
Да — полный текст урока «withUnsafeBytes и взаимодействие с C» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «withUnsafeBytes и взаимодействие с C»?
Подключайтесь к API C, ожидающим необработанные байты. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «withUnsafeBytes и взаимодействие с C»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- MemoryLayout и выравнивание
- UnsafePointer и UnsafeMutablePointer
- Указатели на небезопасные буферы
- withUnsafeBytes и взаимодействие с C