Swift Academy · Lekcja

Niebezpieczne wskaźniki buforów

Wydajne przetwarzanie ciągłego obszaru pamięci.

Lekcja 3 z 413 kroki

Niebezpieczne wskaźniki buforów to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 3 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.

Czym jest wskaźnik bufora

Pojedynczy wskaźnik typowany wskazuje jednorazowo jeden element. UnsafeBufferPointer<T> opakowuje wskaźnik początkowy oraz liczbę elementów, zapewniając ograniczony widok na ciągły blok pamięci zgodny z Collection.

Oznacza to, że można wykonywać iterację, używać indeksowania, map i reduce — korzystając ze wszystkich znanych interfejsów kolekcji na surowej pamięci.

// A buffer pointer = base address + element count
// UnsafeBufferPointer<Int>(start: ptr, count: 5)

Bufory tylko do odczytu a bufory modyfikowalne

Podobnie jak pojedyncze wskaźniki, bufory występują w dwóch odmianach: UnsafeBufferPointer<T> do iteracji tylko do odczytu oraz UnsafeMutableBufferPointer<T>, gdy trzeba również przypisywać elementy.

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

Iterowanie jak po kolekcji

Ponieważ wskaźnik bufora jest obiektem typu Collection, pętla for przechodzi po każdym elemencie w kolejności. Bez ręcznego obliczania indeksów i bez ryzyka błędu o jeden w samej pętli — jednak za podane granice nadal odpowiada użytkownik.

let buf = UnsafeMutableBufferPointer<Int>.allocate(capacity: 3)
_ = buf.initialize(from: [10, 20, 30])
for value in buf { print(value) } // 10, 20, 30

Algorytmy kolekcji bez dodatkowego nakładu

Operacje map, filter, reduce, enumerated i indices działają na wskaźnikach bufora. Dzięki temu można przetwarzać surową pamięć za pomocą kodu wysokiego poziomu, nie powodując alokacji.

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)

Alokowanie modyfikowalnego bufora

UnsafeMutableBufferPointer<T>.allocate(capacity:) rezerwuje blok i zwraca bufor w jednym kroku. Nadal trzeba wykonać initialize przed odczytem oraz deallocate po zakończeniu pracy.

let buf = UnsafeMutableBufferPointer<Int>.allocate(capacity: 5)
buf.initialize(repeating: 0)
defer {
    buf.deinitialize()
    buf.deallocate()
}

Granice nie są sprawdzane automatycznie

Indeksowanie w zakresie 0..<count jest bezpieczne. Jednak bufor nie może wiedzieć, czy podana liczba elementów była poprawna. Jeśli utworzysz bufor z liczbą większą niż rzeczywiście zaalokowana pamięć, odczyty wyjdą poza jej koniec i sięgną do niezdefiniowanej pamięci.

Zawsze wyznaczaj liczbę elementów na podstawie tej samej alokacji, którą utworzysz.

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)

Puste bufory

Bufor z count: 0 jest prawidłowy i może mieć adres bazowy nil. Zawsze sprawdzaj isEmpty lub pozwól, aby pętla po prostu się nie wykonała, zamiast wymuszać rozpakowanie baseAddress.

let empty = UnsafeBufferPointer<Int>(start: nil, count: 0)
print(empty.isEmpty)       // true
print(empty.count)         // 0
for _ in empty { print("never runs") }

withUnsafeBufferPointer w tablicach

Najbezpieczniej uzyskać bufor z istniejącej tablicy Array za pomocą withUnsafeBufferPointer. Swift gwarantuje, że pamięć jest ciągła i prawidłowa przez cały czas wykonywania closure, więc można całkowicie uniknąć ręcznej alokacji.

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

Modyfikowanie pamięci tablicy

withUnsafeMutableBufferPointer pozwala bezpośrednio zapisywać dane w pamięci tablicy. Rozwiązanie to bywa używane w wymagających wydajności transformacjach wykonywanych w miejscu, w których ARC i sprawdzanie granic powodowałyby dodatkowy narzut.

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 w API języka C

Wywołując funkcję C, która oczekuje wskaźnika i długości, przekaż buffer.baseAddress oraz buffer.count. Adres bazowy ma wartość nil tylko dla pustych buforów, więc obsłuż ten przypadek.

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

Kiedy używać wskaźników bufora

Używaj wskaźników bufora, gdy potrzebujesz ciągłego, bezpiecznego typowo dostępu do obszaru pamięci w stylu kolekcji: podczas łączenia tablic C, analizowania formatów binarnych lub ograniczania narzutu w często wykonywanych pętlach. Zawsze, gdy to możliwe, preferuj formy closure with... zamiast ręcznej alokacji.

// 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

Szybkie sprawdzenie

Przypomnij sobie, co wskaźnik bufora oferuje w porównaniu z pojedynczym wskaźnikiem.

Podsumowanie

Wskaźniki bufora opakowują ciągły obszar pamięci jako ograniczoną kolekcję:

  • UnsafeBufferPointer / UnsafeMutableBufferPointer = adres bazowy + liczba elementów.
  • Otrzymujesz operacje map, filter, reduce oraz iterację za pomocą for na surowej pamięci.
  • Preferuj withUnsafeBufferPointer dla tablic, aby Swift zarządzał prawidłowością i czasem życia pamięci.
  • Podana liczba elementów jest uznawana za poprawną — wyznaczaj ją na podstawie rzeczywistej alokacji.
Bezpłatny start

Ucz się Swift dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
122
Lekcje
409

Często zadawane pytania

Czy lekcja „Niebezpieczne wskaźniki buforów” jest bezpłatna?

Tak — pełny tekst „Niebezpieczne wskaźniki buforów” 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 „Niebezpieczne wskaźniki buforów”?

Wydajne przetwarzanie ciągłego obszaru pamięci. Ć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 3 z 4.

Ile czasu zajmuje lekcja „Niebezpieczne wskaźniki buforów”?

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