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) // 99Alokowanie 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 elementInicjalizacja 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 automaticallyArytmetyka 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 directlywithUnsafePointer 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) // 101Typowe pułapki
Wskaźniki typowane są potężne, ale nie wybaczają błędów:
- Odczyt
pointeeprzed wykonanieminitializejest niezdefiniowany. - Pominięcie
deinitializew przypadku typów przechowujących zasoby prowadzi do wycieków. - Pozwolenie, aby wskaźnik
withUnsafePointerwydostał 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 behaviorMał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:
UnsafePointerodczytuje dane, aUnsafeMutablePointerje odczytuje i zapisuje.pointeezapewnia 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
- MemoryLayout i wyrównanie
- UnsafePointer i UnsafeMutablePointer
- Niebezpieczne wskaźniki buforów
- withUnsafeBytes i integracja z C