MemoryLayout i wyrównanie
Sprawdzanie rozmiaru, kroku i wyrównania typów.
MemoryLayout i wyrównanie to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 1 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.
Dlaczego układ pamięci ma znaczenie
Podczas pracy z unsafe Swift lub integracji z C trzeba wiedzieć, jak wartości są rozmieszczone w pamięci. Swift udostępnia MemoryLayout<T>, aby można było to sprawdzić bez dotykania surowych bajtów.
Odpowiada ono na trzy pytania: jaki rozmiar ma wartość, jaka jest odległość między wartościami w tablicy oraz na jakiej granicy adresu musi zaczynać się wartość.
// MemoryLayout works on any type
print(MemoryLayout<Int>.size) // 8 on 64-bit
print(MemoryLayout<Bool>.size) // 1
print(MemoryLayout<Double>.size) // 8size — Rzeczywiste przechowywanie
MemoryLayout<T>.size to liczba bajtów zajmowanych przez istotne dane wartości. Nie obejmuje końcowego dopełnienia, które może znajdować się między elementami tablicy.
Należy używać size, gdy trzeba ustalić, ile znaczących bajtów zawiera pojedyncza wartość.
struct Point {
var x: Int8 // 1 byte
var y: Int64 // 8 bytes
}
print(MemoryLayout<Point>.size) // 16 (not 9 — padding!)stride — Odstępy w tablicach
stride to odległość w bajtach między początkiem jednego elementu a początkiem następnego, gdy wartości są umieszczone w ciągłej tablicy. Jest równa wartości size zaokrąglonej w górę do granicy wyrównania.
Podczas ręcznego alokowania buforów należy mnożyć przez stride, nigdy przez size.
print(MemoryLayout<Point>.stride) // 16
print(MemoryLayout<Int8>.stride) // 1
// Buffer for 10 Points needs:
let bytes = 10 * MemoryLayout<Point>.stride // 160alignment — Granice adresów
alignment to granica bajtowa, na której musi rozpocząć się wartość. Int64 o wyrównaniu 8 musi zaczynać się pod adresem podzielnym przez 8. Kompilator wstawia dopełnienie, aby to zagwarantować.
Wyrównanie struktury jest największym wyrównaniem spośród jej składowych.
print(MemoryLayout<Int8>.alignment) // 1
print(MemoryLayout<Int64>.alignment) // 8
print(MemoryLayout<Point>.alignment) // 8Różnica między size a stride
Dla wielu typów size == stride, ale wartości te różnią się, gdy występuje dopełnienie. Struktura może mieć size równe 9, a stride równe 16, ponieważ następny element musi być wyrównany do 8 bajtów.
Zasada praktyczna: pojedynczą wartość należy analizować za pomocą size, a kolekcje za pomocą stride.
struct Mixed {
var flag: Bool // 1
var value: Int // 8
}
print(MemoryLayout<Mixed>.size) // 16
print(MemoryLayout<Mixed>.stride) // 16Przesunięcia pól
W przypadku struktur można sprawdzić, gdzie znajduje się każda przechowywana właściwość względem początku, używając MemoryLayout<T>.offset(of:) wraz ze ścieżką kluczową. Jest to niezbędne podczas układania danych tak, aby odpowiadały strukturze C.
struct Header {
var magic: UInt32 // offset 0
var length: UInt64 // offset 8 (padded)
}
print(MemoryLayout<Header>.offset(of: \Header.magic) ?? -1) // 0
print(MemoryLayout<Header>.offset(of: \Header.length) ?? -1) // 8Zmiana kolejności w celu oszczędzania miejsca
Ze względu na wyrównanie kolejność przechowywanych właściwości wpływa na rozmiar. Umieszczenie większych pól na początku lub grupowanie ich według rozmiaru może zmniejszyć strukturę. Ma to znaczenie w przypadku dużych tablic, w których zmarnowane bajty szybko się sumują.
struct Wasteful { var a: Int8; var b: Int64; var c: Int8 } // size 24
struct Tight { var b: Int64; var a: Int8; var c: Int8 } // size 16
print(MemoryLayout<Wasteful>.size)
print(MemoryLayout<Tight>.size)Układ referencji
Instancja klasy jest alokowana na stercie, więc zmienna przechowująca referencję do klasy jest po prostu wskaźnikiem. Jej MemoryLayout.size odpowiada rozmiarowi wskaźnika (8 bajtów), niezależnie od rozmiaru obiektu.
Typy wartości, takie jak struktury, przechowują zawartość bezpośrednio, natomiast typy referencyjne przechowują tylko wskaźnik.
class Big { var data = [Int](repeating: 0, count: 1000) }
print(MemoryLayout<Big>.size) // 8 — just the referenceTypy opcjonalne i wyliczenia
Typy opcjonalne zwykle dodają bajt na znacznik, ale Swift optymalizuje opcjonalne referencje, wykorzystując ponownie pusty wskaźnik, dzięki czemu String? może mieć taki sam rozmiar jak String.
Wyliczenia z wartościami powiązanymi mają rozmiar wystarczający na przechowanie największego przypadku oraz znacznika rozróżniającego.
print(MemoryLayout<Int?>.size) // 9
print(MemoryLayout<String>.size) // 16
print(MemoryLayout<String?>.size) // 16 — no extra byteUżywanie of: z instancją
Każdy element (size, stride, alignment) ma również formę opartą na instancji: MemoryLayout.size(ofValue:). Jest to przydatne, gdy trudno jawnie zapisać typ lub gdy jest on wywnioskowany.
let tuple = (UInt8(1), UInt64(2))
print(MemoryLayout.size(ofValue: tuple)) // 16
print(MemoryLayout.stride(ofValue: tuple)) // 16
print(MemoryLayout.alignment(ofValue: tuple)) // 8Alokowanie z poprawnym układem
Podczas alokowania surowej pamięci należy przekazać zarówno liczbę bajtów, jak i wyrównanie. Pobranie tych wartości z MemoryLayout gwarantuje poprawność na różnych architekturach, zamiast wymagać wpisywania stałych liczb.
let count = 4
let capacity = count * MemoryLayout<Int>.stride
let align = MemoryLayout<Int>.alignment
let raw = UnsafeMutableRawPointer.allocate(
byteCount: capacity, alignment: align)
defer { raw.deallocate() }Szybkie sprawdzenie
Sprawdź swoje rozumienie różnicy między size a stride.
Podsumowanie
Poznali Państwo trzy filary układu pamięci:
- size — znaczące bajty pojedynczej wartości
- stride — odstęp między elementami tablicy (należy go używać podczas alokowania)
- alignment — wymagana granica początku
Poznali Państwo również offset(of:) do określania pozycji pól, wpływ kolejności właściwości na rozmiar oraz fakt, że referencje mają rozmiar wskaźnika. Narzędzia te umożliwiają bezpieczne alokowanie i interpretowanie surowej pamięci.
Często zadawane pytania
Czy lekcja „MemoryLayout i wyrównanie” jest bezpłatna?
Tak — pełny tekst „MemoryLayout i wyrównanie” 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 „MemoryLayout i wyrównanie”?
Sprawdzanie rozmiaru, kroku i wyrównania typó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 1 z 4.
Ile czasu zajmuje lekcja „MemoryLayout i wyrównanie”?
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