2016-09-03 14 views
6

W języku C kolejność, w której definiowane są pola w strukturze, jest kolejnością, w której będą one tworzone w pamięci. Biorąc pod uwagę wyrównanie pamięci, następująca struktura będzie miała rozmiar 8 bajtów w pamięci, jak pokazano, ale tylko 6 bajtów, jeśli pola są odwrócone, ponieważ nie ma potrzeby wypełniania wyrównania.Czy Swift gwarantuje kolejność przechowywania pól w klasach i klasach?

struct s { 
    int32_t a; 
    /* 2 bytes of padding to align a 64 bit integer */ 
    int64_t b; 
} 

Ta gwarancja zamawiania jest obecna w klasach C, klasach C++ (i klasach) oraz klasach Objective-C.

Czy kolejność przechowywania jest podobnie gwarantowana dla pól w klasach Swift i strukturach? Lub (biorąc pod uwagę, że język nie obsługuje wskaźników w taki sam sposób, jak inne wymienione), czy kompilator optymalnie rozmieści je dla Ciebie podczas kompilacji?

+0

Dlaczego polegasz na położeniu właściwości obiektu? Co chcesz zrobić? – FredericP

+0

Nie chcę nic z tym robić, tylko dlatego, że gdy zaczniesz tworzyć [m | b] iliony małych struktur, różnica zaczyna się sumować, szczególnie na urządzeniach mobilnych. – Ephemera

Odpowiedz

12

Tak, kolejność elementów struct w pamięci jest zgodna z ich deklaracją w następującej kolejności: . Szczegóły można znaleźć w The Swift ABI (podkreślenie dodane). Należy jednak pamiętać, korzystanie z „obecnie”, więc ten mogą ulec zmianie w przyszłych wersjach SWIFT:

Fragile Struct i krotka Układ

elemencie krotkach obecnie dzielą ten sam algorytm układ, zwany algorytmem "Universal" w implementacji kompilatora. Algorytm jest następujący:

  • start o rozmiarze 0 i dostosowania 1.
  • iterację pól , w celu element krotki lub var w deklaracji celu dla elemencie. Dla każdego pola:
    • wielkości Update, zaokrąglając w górę do wyrównania pola, czyli zwiększenie go do najmniejszej wartości większej lub równej wielkości i podzielny przez wyrównanie pola.
    • Przypisz przesunięcie pola do bieżącej wartości rozmiaru.
    • Zaktualizuj rozmiar, dodając rozmiar pola.
    • Uaktualnij wyrównanie do maksymalnie wyrównania i wyrównania pola.
  • Ostateczny rozmiar i wyrównanie są wielkością i wyrównaniem agregatu. Krokiem tego typu jest końcowy rozmiar zaokrąglony do wyrównania.

Wyściółka/ustawienie jest różne od C:

Należy zauważyć, że różni się od normalnych zasad układu C albo llvm na tym, że wielkość i kroku są różne; mając na uwadze, że układ C wymaga, aby rozmiar osadzonej struktury był wyrównywany do jego wyrównania i aby nic nie było tam układane, układ Swift umożliwia zewnętrznym strukturom rozmieszczenie pól w wewnętrznym obramowaniu klocka wewnętrznego, przy dozwoleniu dopasowania.

Tylko jeśli struktura jest importowane od C, a następnie jest ona gwarantowana mieć takim samym układzie pamięci. Joe Groff od Apple pisze w [swift-users] Mapping C semantics to Swift

Jeżeli zależy od konkretnego układu, należy zdefiniować struct w C i zaimportować go do Swift teraz.

i later in that discussion:

Możesz zostawić struct zdefiniowane w C i zaimportować go do Swift. Swift przestrzega układu C.

przykład:

struct A { 
    var a: UInt8 = 0 
    var b: UInt32 = 0 
    var c: UInt8 = 0 
} 

struct B { 
    var sa: A 
    var d: UInt8 = 0 
} 

// Swift 2: 
print(sizeof(A), strideof(A)) // 9, 12 
print(sizeof(B), strideof(B)) // 10, 12 

// Swift 3: 
print(MemoryLayout<A>.size, MemoryLayout<A>.stride) // 9, 12 
print(MemoryLayout<B>.size, MemoryLayout<B>.stride) // 10, 12 

tutaj var d: UInt8 jest rozplanowane w wyściółki ogonowej var sa: A. Jeżeli określenie samych struktur C

struct CA { 
    uint8_t a; 
    uint32_t b; 
    uint8_t c; 
}; 

struct CB { 
    struct CA ca; 
    uint8_t d; 
}; 

i import do Swift następnie

// Swift 2: 
print(sizeof(CA), strideof(CA)) // 9, 12 
print(sizeof(CB), strideof(CB)) // 13, 16 

// Swift 3: 
print(MemoryLayout<CA>.size, MemoryLayout<CA>.stride) // 12, 12 
print(MemoryLayout<CB>.size, MemoryLayout<CB>.stride) // 16, 16 

ponieważ uint8_t d jest rozplanowane po wyściółki ogonowej struct CA sa.

jako Swift 3, zarówno size i stride powrót tą samą wartość (tym wyściółka struktura) do struktur importowane z C, to znaczy taką samą wartość jak sizeof w C wrócimy.

Oto prosta funkcja, która pozwala wykazać powyżej (Swift 3)

func showMemory<T>(_ ptr: UnsafePointer<T>) { 
    let data = Data(bytes: UnsafeRawPointer(ptr), count: MemoryLayout<T>.size) 
    print(data as NSData) 
} 

strukturami definiowanymi w SWIFT

var a = A(a: 0xaa, b: 0xbbbbbbbb, c: 0xcc) 
showMemory(&a) // <aa000000 bbbbbbbb cc> 

var b = B(sa: a, d: 0xdd) 
showMemory(&b) // <aa000000 bbbbbbbb ccdd> 

Struktury importowane z C:

var ca = CA(a: 0xaa, b: 0xbbbbbbbb, c: 0xcc) 
showMemory(&ca) // <aa000000 bbbbbbbb cc000000> 

var cb = CB(ca: ca, d: 0xdd) 
showMemory(&cb) // <aa000000 bbbbbbbb cc000000 dd000000> 
+1

Bardzo wszechstronny, dziękuję bardzo! – Ephemera

1

Wygląda na to, że zamówienie jest zagwarantowane.

Biorąc pod uwagę następujące struct:

struct s { 
    var a: UInt32 
    var c: UInt8 
    var b: UInt64 
} 

sizeof(s) // 16 

Wyrównanie jest nadal dzieje. Tam jest utracone 8 bitów między c i b.

Oczywiście, to nie jest jasne, czy te bity są rzeczywiście między c & b lub po prostu dołączona na końcu ... dopóki nie zmienić:

struct s { 
    var a: UInt32 
    var b: UInt64 
    var c: UInt8 
} 

sizeof(s) // 17 

Uważam takie zachowanie pasuje do innych języków wymienionych w wy Twoje pytanie.

+0

"Brakuje 8 bitów ..." Myślę, że masz na myśli 3 bajty. – RenniePet

Powiązane problemy