0Pricing
Swift Academy · Lekcja

UnsafePointer i UnsafeMutablePointer

Ostrożne odczytywanie i zapisywanie surowej pamięci.

UnsafePointer i UnsafeMutablePointer to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 2 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.

Wskaźniki typowane w Swift

Bezpieczne referencje języka Swift nie udostępniają surowych adresów, ale podczas integracji z C lub tworzenia niskopoziomowych struktur danych można użyć wskaźników typowanych: UnsafePointer<T> do dostępu tylko do odczytu oraz UnsafeMutablePointer<T> do odczytu i zapisu.

Wskaźnik typowany zna typ elementu, dlatego może obliczać odstępy i odczytywać wartości o właściwym typie.

// A pointer to an Int we can read but not write
func readFirst(_ p: UnsafePointer<Int>) -> Int {
    return p.pointee
}

Właściwość pointee

pointee to wartość, na którą aktualnie wskazuje wskaźnik. Za pomocą wskaźnika modyfikowalnego można ją zarówno odczytywać, jak i przypisywać; za pomocą wskaźnika tylko do odczytu można ją wyłącznie odczytywać.

Odwołanie do pointee w niezainicjalizowanej lub zwolnionej pamięci jest niezdefiniowanym zachowaniem — system typów tego nie wykryje.

let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
p.initialize(to: 42)
print(p.pointee)   // 42
p.pointee = 99
print(p.pointee)   // 99

Alokowanie pamięci

UnsafeMutablePointer<T>.allocate(capacity:) rezerwuje miejsce na elementy w liczbie capacity, ale ich nie inicjalizuje. Pamięć jest surowa, a odczyt przed inicjalizacją jest niedozwolony.

// Reserve space for 3 Doubles (uninitialized)
let buffer = UnsafeMutablePointer<Double>.allocate(capacity: 3)
// Must initialize before reading any element

Inicjalizacja i deinicjalizacja

Przed odczytem należy wykonać initialize. Przed zwolnieniem pamięci typów wymagających sprzątania, takich jak klasy lub napisy, należy wykonać deinitialize, aby zwolnić zajmowane przez nie zasoby.

Cykl życia wygląda następująco: allocate → initialize → use → deinitialize → deallocate.

let p = UnsafeMutablePointer<String>.allocate(capacity: 1)
p.initialize(to: "hello")
print(p.pointee)
p.deinitialize(count: 1) // releases the String
p.deallocate()

Zawsze łącz allocate z deallocate

Każde allocate musi mieć dokładnie jedno odpowiadające mu deallocate, w przeciwnym razie dojdzie do wycieku pamięci. Użycie defer bezpośrednio po alokacji jest najbezpieczniejszym wzorcem, ponieważ wykona się niezależnie od sposobu zakończenia funkcji.

func process() {
    let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    defer { p.deallocate() }
    p.initialize(to: 7)
    print(p.pointee)
} // deallocate runs here automatically

Arytmetyka wskaźników

Wskaźniki typowane obsługują indeksowanie i arytmetykę w jednostkach elementów, a nie bajtów. p + 2 przesuwa wskaźnik o dwa odstępy typu T. p[i] to skrót zapisu (p + i).pointee.

let p = UnsafeMutablePointer<Int>.allocate(capacity: 3)
p.initialize(repeating: 0, count: 3)
p[0] = 10
p[1] = 20
p[2] = 30
print((p + 1).pointee) // 20
p.deinitialize(count: 3)
p.deallocate()

Inicjalizacja wielu elementów

W przypadku bloku pamięci initialize(repeating:count:) ustawia każdą pozycję na tę samą wartość, natomiast initialize(from:count:) kopiuje dane z innego bufora. Obie metody w pełni inicjalizują zakres, dzięki czemu jego odczyt jest bezpieczny.

let dst = UnsafeMutablePointer<Int>.allocate(capacity: 4)
dst.initialize(repeating: 5, count: 4)
print(dst[3]) // 5
dst.deinitialize(count: 4)
dst.deallocate()

Tylko do odczytu a modyfikowalny

Funkcja, która tylko sprawdza dane, powinna przyjmować UnsafePointer<T>, a funkcja, która je modyfikuje — UnsafeMutablePointer<T>. Wskaźnik modyfikowalny jest niejawnie konwertowany na wskaźnik tylko do odczytu, ale nie odwrotnie — wymusza to zamierzone użycie na granicy API.

func sum(_ p: UnsafePointer<Int>, _ n: Int) -> Int {
    var total = 0
    for i in 0..<n { total += p[i] }
    return total
}
// A mutable pointer can be passed here directly

withUnsafePointer dla zmiennych lokalnych

Aby uzyskać tymczasowy wskaźnik do istniejącej wartości bez alokowania pamięci, należy użyć withUnsafePointer(to:) lub jego modyfikowalnego odpowiednika. Wskaźnik jest ważny tylko wewnątrz closure — nigdy nie należy pozwalać mu wydostać się poza ten zakres.

var value = 100
withUnsafeMutablePointer(to: &value) { ptr in
    ptr.pointee += 1
}
print(value) // 101

Typowe pułapki

Wskaźniki typowane są potężne, ale nie wybaczają błędów:

  • Odczyt pointee przed wykonaniem initialize jest niezdefiniowany.
  • Pominięcie deinitialize w przypadku typów przechowujących zasoby prowadzi do wycieków.
  • Pozwolenie, aby wskaźnik withUnsafePointer wydostał się poza closure, powoduje powstanie wiszącego wskaźnika.

Kompilator ufa tutaj Państwa kodowi — w czasie wykonywania nie ma żadnych mechanizmów ochronnych.

// DANGEROUS — pointer escapes the closure
// var escaped: UnsafePointer<Int>?
// withUnsafePointer(to: &x) { escaped = $0 }
// escaped!.pointee  // dangling, undefined behavior

Mały stos zarządzany ręcznie

Połączenie wszystkich elementów wygląda następująco: należy zaalokować blok, traktować go jak elementy z użyciem indeksowania, a następnie posprzątać. To podstawa niestandardowych kolekcji i integracji z C.

let n = 3
let stack = UnsafeMutablePointer<Int>.allocate(capacity: n)
stack.initialize(repeating: 0, count: n)
for i in 0..<n { stack[i] = i * i }
// stack now holds 0, 1, 4
stack.deinitialize(count: n)
stack.deallocate()

Szybkie sprawdzenie

Przypomnij sobie poprawny cykl życia wskaźnika.

Podsumowanie

Poznali Państwo wskaźniki typowane:

  • UnsafePointer odczytuje dane, a UnsafeMutablePointer je odczytuje i zapisuje.
  • pointee zapewnia dostęp do wartości, a arytmetyka odbywa się w jednostkach elementów.
  • Cykl życia wygląda następująco: allocate → initialize → use → deinitialize → deallocate, najlepiej z użyciem defer.

W dalszej części zobaczą Państwo, jak wskaźniki buforów opakowują cały ciągły obszar w interfejs przyjazny kolekcjom.

Często zadawane pytania

Czy lekcja „UnsafePointer i UnsafeMutablePointer” jest bezpłatna?

Tak — pełny tekst „UnsafePointer i UnsafeMutablePointer” 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 „UnsafePointer i UnsafeMutablePointer”?

Ostrożne odczytywanie i zapisywanie surowej 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 2 z 4.

Ile czasu zajmuje lekcja „UnsafePointer i UnsafeMutablePointer”?

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