0Pricing
Swift Academy · Lekcja

withUnsafeBytes i integracja z C

Łączenie z API C wymagającymi surowych bajtów.

withUnsafeBytes i integracja z C to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Swift Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Swift Academy zawiera 4 lekcji w sumie.

Od typowanych danych do surowych bajtów

Czasami trzeba zobaczyć wartość nie jako Int czy struct, lecz jako sekwencję surowych bajtów — aby ją zahaszować, zapisać w pliku lub przekazać do API C. withUnsafeBytes zapewnia dokładnie taki widok: tymczasowy widok pamięci wartości jako UInt8.

var value: UInt32 = 0x01020304
withUnsafeBytes(of: &value) { rawBuffer in
    print(rawBuffer.count) // 4 bytes
}

Typ UnsafeRawBufferPointer

Closure otrzymuje obiekt UnsafeRawBufferPointer — bufor bajtów bez typu elementów. Można używać indeksowania, aby odczytywać pojedyncze wartości UInt8. Jest on tylko do odczytu; odpowiednikiem modyfikowalnym jest withUnsafeMutableBytes.

var value: UInt32 = 0x01020304
withUnsafeBytes(of: &value) { bytes in
    for b in bytes { print(String(format: "%02x", b)) }
}
// Order depends on endianness

Znaczenie kolejności bajtów

Kolejność bajtów, którą obserwujesz, zależy od kolejności bajtów platformy. Na maszynach little-endian (Apple silicon, Intel) najmniej znaczący bajt występuje jako pierwszy. Podczas serializowania danych na potrzeby protokołu jawnie konwertuj kolejność za pomocą bigEndian lub 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))
}

Bajty struktury

withUnsafeBytes(of:) działa z każdą wartością, w tym ze strukturami. Długość bufora jest równa MemoryLayout<T>.size, a bajty dopełnienia są uwzględnione, choć ich zawartość jest nieokreślona.

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
}

Wczytywanie wartości z bajtów

Aby zinterpretować surowe bajty jako wartość określonego typu, użyj na surowym buforze funkcji load(fromByteOffset:as:). Przesunięcie musi być zgodne z wymaganym wyrównaniem typu, a bajty muszą rzeczywiście przedstawiać prawidłową wartość tego typu.

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 dla spakowanych danych

Formaty binarne często upakowują pola bez dopełnienia wyrównującego. Wywołanie load przy niewyrównanym przesunięciu jest niezdefiniowanym zachowaniem; zamiast tego użyj loadUnaligned(fromByteOffset:as:), aby bezpiecznie odczytać dane z dowolnego przesunięcia.

let packet: [UInt8] = [0xFF, 0x04, 0x03, 0x02, 0x01]
let field = packet.withUnsafeBytes { ptr in
    ptr.loadUnaligned(fromByteOffset: 1, as: UInt32.self)
}
print(field)

Przekazywanie Swift Data do C

Wiele funkcji C przyjmuje const void * oraz długość. Data i tablice udostępniają withUnsafeBytes, dzięki czemu można bezpośrednio przekazać baseAddress i count, bez kopiowania.

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)
    }
}

Odbieranie wskaźnika C

Gdy callback C przekazuje const void * i długość, opakuj go w UnsafeRawBufferPointer, aby bezpiecznie wykonywać iterację. Swift zaimportuje wskaźnik C jako 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 i reinterpretacja

Aby traktować surowy obszar jako elementy określonego typu, bindMemory(to:capacity:) informuje kompilator, jak go interpretować. Niewłaściwe użycie tej funkcji — powiązanie tej samej pamięci z dwoma niezgodnymi typami — narusza ścisłe reguły aliasingu Swift i prowadzi do niezdefiniowanego zachowania.

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 a bindMemory

bindMemory trwale zmienia sposób interpretowania pamięci, natomiast assumingMemoryBound(to:) zakłada, że pamięć jest już powiązana z tym typem, bez wprowadzania jakichkolwiek zmian. Tej drugiej funkcji używaj tylko wtedy, gdy masz pewność co do istniejącego powiązania, na przykład w przypadku pamięci, o której wiesz, że zawiera wartości danego typu.

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
}

Lista kontrolna bezpieczeństwa

Surowe bajty i współpraca z C to miejsca, w których najczęściej ukrywają się awarie. Pamiętaj:

  • Nigdy nie pozwalaj, aby wskaźnik z closure wydostał się poza jej zakres.
  • Przestrzegaj wyrównania lub używaj wariantów Unaligned.
  • Jawnie obsługuj endianness podczas serializacji.
  • Nie wiąż pamięci z niezgodnymi typami.
  • W przypadku pustych buforów zabezpiecz się przed wartością nil adresu baseAddress.
// 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()
    }
}

Szybkie sprawdzenie

Sprawdź, jak dobrze rozumiesz odczytywanie spakowanych danych binarnych.

Podsumowanie

Nauczyłeś się łączyć wartości Swift i C za pomocą surowych bajtów:

  • withUnsafeBytes(of:) przedstawia dowolną wartość jako UInt8.
  • load / loadUnaligned reinterpretują bajty jako wartości określonych typów, z uwzględnieniem wyrównania.
  • Data i tablice udostępniają withUnsafeBytes, aby przekazywać do C wskaźnik i długość.
  • bindMemory i assumingMemoryBound określają sposób typowania surowej pamięci.
  • Kolejność bajtów, wyrównanie i wydostanie się wskaźnika poza closure to główne zagrożenia.

Często zadawane pytania

Czy lekcja „withUnsafeBytes i integracja z C” jest bezpłatna?

Tak — pełny tekst „withUnsafeBytes i integracja z C” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Swift Academy, przejdź na CoddyKit PRO. Kurs Swift Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „withUnsafeBytes i integracja z C”?

Łączenie z API C wymagającymi surowych bajtów. Ćwiczysz Swift Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Swift Academy?

Nie wymagamy żadnego doświadczenia. Swift Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „withUnsafeBytes i integracja z C”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Swift Academy?

Tak. Każda lekcja Swift Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. MemoryLayout i wyrównanie
  2. UnsafePointer i UnsafeMutablePointer
  3. Niebezpieczne wskaźniki buforów
  4. withUnsafeBytes i integracja z C
← Powrót do Swift Academy