# Swift MemoryLayout and Alignment: Size, Stride, and Padding

Source: https://devdocs10.com/articles/swift/swift-memorylayout-alignment  
By Murat Bo · Published 2026-06-18 · DevDocs10 (https://devdocs10.com)

Swift’s `MemoryLayout` type tells you how much memory a type uses, how its values are aligned, and how much space each value occupies in an array. These details matter when you work with unsafe memory, performance-sensitive code, binary data, or interoperability with lower-level APIs.

> **Quick answer:** `MemoryLayout` gives you three important measurements: `size` is the actual bytes used by a value, `stride` is the spacing between consecutive values in an array, and `alignment` is the byte boundary the type prefers. `stride` is often larger than `size` because of padding.

> **Difficulty:** Intermediate
>
> **You'll understand this better if you know:** basic Swift structs, arrays, stored properties, and the difference between value types and reference types.

## 1. What Is MemoryLayout?

`MemoryLayout` is a generic Swift standard library type that describes how a type is represented in memory. It is most often used to inspect the memory characteristics of structs, enums, tuples, and primitive types such as `Int` and `Double`.

- `size` is the number of bytes needed to store a single value.
- `stride` is the distance from one value to the next in memory.
- `alignment` is the byte boundary the type should start on.

These values are not always the same. In particular, the `stride` can include extra padding bytes so that the next value in an array is correctly aligned.

## 2. Why MemoryLayout Matters

Most Swift code never needs to think about raw bytes, but `MemoryLayout` becomes important when you:

- read or write binary files
- interact with unsafe pointers or raw memory
- optimize memory use in large data structures
- compare the storage cost of different types
- debug layout-related issues in low-level code

It also helps explain why two types that appear similar can have very different memory costs. A struct with a small `size` can still have a larger `stride` because Swift must insert padding to keep values aligned efficiently.

## 3. Basic Syntax

You use `MemoryLayout<Type>` to inspect a type at compile time. The three most common properties are static and easy to read.

### Reading size, stride, and alignment

```swift
struct Pixel {
    var red: UInt8
    var green: UInt8
    var blue: UInt8
}

print(MemoryLayout<Pixel>.size)
print(MemoryLayout<Pixel>.stride)
print(MemoryLayout<Pixel>.alignment)
```

Output (tested with Swift 6.4):

```text
3
3
1
```

This example shows the basic pattern. You provide a type inside angle brackets, then ask for the measurement you need.

### Value-level size

Swift also provides a slightly different API for instances:

```swift
let pixel = Pixel(red: 255, green: 128, blue: 64)
print(MemoryLayout.size(ofValue: pixel))
```

For most layout questions, the type-based form is the one you want because it describes the type itself rather than one specific instance.

## 4. Step-by-Step Examples

### Example 1: A simple struct with no padding surprises

Start with three one-byte fields. This is a good baseline because the values are already naturally compact.

```swift
struct RGB {
    var r: UInt8
    var g: UInt8
    var b: UInt8
}

print(MemoryLayout<RGB>.size)      // 3
print(MemoryLayout<RGB>.stride)    // 3 or more, depending on alignment needs
print(MemoryLayout<RGB>.alignment) // typically 1
```

Output (tested with Swift 6.4):

```text
3
3
1
```

Because each property is a `UInt8`, the struct stores exactly three bytes of data. The stride is often the same here, because the alignment requirement is small.

### Example 2: A struct with padding

Now add a larger type. This is where padding becomes visible.

```swift
struct Record {
    var flag: UInt8
    var count: Int
}

print(MemoryLayout<Record>.size)
print(MemoryLayout<Record>.stride)
print(MemoryLayout<Record>.alignment)
```

Output (tested with Swift 6.4):

```text
16
16
8
```

The `Int` property usually needs a stricter alignment than `UInt8`. Swift may insert padding between `flag` and `count` so that `count` starts on a suitable boundary.

### Example 3: Reordering properties to reduce padding

Property order can affect memory layout. Putting larger fields first often reduces wasted space.

```swift
struct PoorLayout {
    var a: UInt8
    var b: Int
    var c: UInt8
}

struct BetterLayout {
    var b: Int
    var a: UInt8
    var c: UInt8
}

print(MemoryLayout<PoorLayout>.stride)
print(MemoryLayout<BetterLayout>.stride)
```

Output (tested with Swift 6.4):

```text
24
16
```

This does not change what the struct means semantically, but it can change how efficiently it fits in memory. In large collections, that difference can matter.

### Example 4: Arrays use stride, not size, between elements

Arrays store values back-to-back using the type’s `stride`. That means the amount of memory used by an array is closer to `count × stride` than `count × size`.

```swift
struct Item {
    var id: UInt8
    var value: Int
}

let items: [Item] = [
    Item(id: 1, value: 100),
    Item(id: 2, value: 200)
]

let estimatedBytes = items.count * MemoryLayout<Item>.stride
print(estimatedBytes)
```

Output (tested with Swift 6.4):

```text
32
```

This example shows why stride is the more useful number when you want to estimate the storage of an array of values.

## 5. Practical Use Cases

- Estimating the memory cost of large arrays of custom structs.
- Choosing property order to reduce padding in storage-heavy models.
- Calculating raw buffer sizes for unsafe APIs.
- Reading binary data into a Swift type only when its layout is appropriate.
- Debugging why a value seems to occupy more space than expected.

In real code, you usually use `MemoryLayout` for inspection and planning, not as part of every day business logic.

## 6. Common Mistakes

### Mistake 1: Using size when you need stride

Beginners often multiply `size` by a collection count and assume that gives the total storage used. That ignores padding between elements.

**Problem:** The calculation underestimates memory because the array steps through elements using stride, not size.

```swift
struct Entry {
    var tag: UInt8
    var amount: Int
}

let count = 10
let wrongBytes = count * MemoryLayout<Entry>.size
```

**Fix:** Use `stride` for storage estimates involving repeated values.

```swift
let correctBytes = count * MemoryLayout<Entry>.stride
```

The corrected version matches how Swift spaces values in memory.

### Mistake 2: Assuming property order never matters

Swift does not sort stored properties for you. If you place small fields between large ones, you can increase padding.

**Problem:** This layout can waste memory because the compiler must insert extra bytes to align the `Int` value.

```swift
struct Wasteful {
    var a: UInt8
    var b: Int
    var c: UInt8
}
```

**Fix:** Group larger alignment requirements first when layout efficiency matters.

```swift
struct Tighter {
    var b: Int
    var a: UInt8
    var c: UInt8
}
```

The corrected version often reduces padding while preserving the same semantic model.

### Mistake 3: Treating MemoryLayout as a promise for all platforms

The exact numbers can vary across architectures and Swift implementations. A value’s layout is stable enough for the current platform, but not always identical everywhere.

**Problem:** Code that hard-codes one platform’s byte counts can fail or read invalid data on another platform.

```swift
let expectedBytes = 16
if MemoryLayout<Int>.size != expectedBytes {
    print("Unexpected platform layout")
}
```

Output (tested with Swift 6.4):

```text
Unexpected platform layout
```

**Fix:** Query the type at runtime and avoid assuming the same byte width on every system.

```swift
let actualBytes = MemoryLayout<Int>.size
print("Int uses \(actualBytes) bytes on this platform")
```

Output (tested with Swift 6.4):

```text
Int uses 8 bytes on this platform
```

The corrected version adapts to the current environment instead of relying on a fixed assumption.

## 7. Best Practices

### Practice 1: Use stride for buffers and arrays

When allocating or estimating repeated storage, prefer `stride`. It matches the spacing rules Swift uses for consecutive values.

```swift
struct Sample {
    var x: UInt8
    var y: Int
}

let bufferSize = 100 * MemoryLayout<Sample>.stride
```

This is the safest choice when values are stored in an array-like pattern.

### Practice 2: Use size for a single in-memory value

If you only need the bytes occupied by one value, `size` is the right measurement.

```swift
struct Header {
    var version: UInt8
    var flags: UInt8
}

let bytesForOneHeader = MemoryLayout<Header>.size
```

This helps when you are packing or unpacking one value into a raw byte sequence.

### Practice 3: Reorder stored properties only when it matters

Property order can improve memory efficiency, but readability still matters. Use ordering changes when a type appears many times or occupies large collections.

```swift
struct LogEntry {
    var timestamp: Int64
    var level: UInt8
    var isArchived: Bool
}
```

A layout-aware order can reduce waste, but you should not sacrifice clarity for a tiny savings in a rarely used type.

## 8. Limitations and Edge Cases

- `MemoryLayout` describes storage characteristics, not semantic meaning. Two types with the same bytes are still different types.
- Layout can differ between platforms, especially for machine-sized types like `Int` and `UInt`.
- Enums may have optimized layouts that are not obvious from their source syntax.
- Swift may use spare bits or internal optimizations, so the raw representation is not always simple.
- You should not assume a Swift struct can be safely serialized by copying its raw bytes unless you fully control the type and format.

**Warning:** Never treat raw memory as a stable file format unless you explicitly define the format yourself. Native Swift layout is for memory, not long-term storage compatibility.

## 9. Practical Mini Project

Let’s build a tiny layout inspector that prints the memory characteristics of a few types. This is a simple but complete example you can adapt during debugging or learning.

```swift
import Foundation

struct Small {
    var a: UInt8
    var b: UInt8
}

struct Mixed {
    var a: UInt8
    var b: Int
}

func describe<T>(_ type: T.Type, name: String) {
    print("\(name):")
    print("  size: \(MemoryLayout<T>.size)")
    print("  stride: \(MemoryLayout<T>.stride)")
    print("  alignment: \(MemoryLayout<T>.alignment)")
}

describe(Small.self, name: "Small")
describe(Mixed.self, name: "Mixed")
describe(Int.self, name: "Int")
```

Output (tested with Swift 6.4):

```text
Small:
  size: 2
  stride: 2
  alignment: 1
Mixed:
  size: 16
  stride: 16
  alignment: 8
Int:
  size: 8
  stride: 8
  alignment: 8
```

This project helps you compare types quickly and spot padding or alignment effects in your own models.

## 10. Key Points

- `size` is the bytes used by one value.
- `stride` is the spacing between values in a collection.
- `alignment` describes the preferred memory boundary for the type.
- Padding can make `stride` larger than `size`.
- Property order can affect layout efficiency.
- Use `MemoryLayout` for inspection, raw memory work, and allocation planning.

## 11. Practice Exercise

Try this exercise to confirm your understanding of layout and alignment.

- Create two structs that contain the same properties in different orders.
- Print `size`, `stride`, and `alignment` for each struct.
- Compare the results and note which ordering produces less padding.
- Then create an array of each type and estimate its storage using `count × stride`.

**Expected output:** You should see that the property order can change `stride`, even when `size` stays close to the same.

**Hint:** Put the largest alignment requirement first, then smaller fields later.

**Solution:**

```swift
struct FirstOrder {
    var a: UInt8
    var b: Int
    var c: UInt8
}

struct SecondOrder {
    var b: Int
    var a: UInt8
    var c: UInt8
}

print("FirstOrder size: \(MemoryLayout<FirstOrder>.size)")
print("FirstOrder stride: \(MemoryLayout<FirstOrder>.stride)")
print("FirstOrder alignment: \(MemoryLayout<FirstOrder>.alignment)")

print("SecondOrder size: \(MemoryLayout<SecondOrder>.size)")
print("SecondOrder stride: \(MemoryLayout<SecondOrder>.stride)")
print("SecondOrder alignment: \(MemoryLayout<SecondOrder>.alignment)")

let firstValues = [
    FirstOrder(a: 1, b: 2, c: 3),
    FirstOrder(a: 4, b: 5, c: 6)
]

let secondValues = [
    SecondOrder(b: 2, a: 1, c: 3),
    SecondOrder(b: 5, a: 4, c: 6)
]

let firstBytes = firstValues.count * MemoryLayout<FirstOrder>.stride
let secondBytes = secondValues.count * MemoryLayout<SecondOrder>.stride

print("FirstOrder array bytes: \(firstBytes)")
print("SecondOrder array bytes: \(secondBytes)")
```

Output (tested with Swift 6.4):

```text
FirstOrder size: 17
FirstOrder stride: 24
FirstOrder alignment: 8
SecondOrder size: 10
SecondOrder stride: 16
SecondOrder alignment: 8
FirstOrder array bytes: 48
SecondOrder array bytes: 32
```

This solution shows how layout can change when fields are reordered and why `stride` is the correct choice for repeated storage.

## 12. Final Summary

`MemoryLayout` is the Swift tool for understanding a type’s memory footprint. Its three measurements—`size`, `stride`, and `alignment`—help you reason about padding, storage cost, and raw memory usage.

In practical Swift code, the most important habit is knowing when to use each measurement. Use `size` for one value, `stride` for repeated values and buffers, and `alignment` when you need to understand how the value must be placed in memory.

If you want to go further, next study Swift unsafe pointers and raw buffers, because that is where `MemoryLayout` becomes especially useful in real projects.
