0Pricing
Swift Academy · Урок

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 — локальная установка не требуется.

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

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