I hope one or more of the SwiftUI team answers this, because I'd like to know the ārealā answers. But here's one answer I'm aware of: using an associated type for body reduces heap allocations.
In your design, a View's body property returns an āexistential containerā, which can hold any object that conforms to the View protocol. TypeLayout.rst describes the memory layout of existential containers:
Existential Container Layout
Values of protocol type, protocol composition type, or Any type are laid out using existential containers (so-called because these types are "existential types" in type theory).
Opaque Existential Containers
If there is no class constraint on a protocol or protocol composition type, the existential container has to accommodate a value of arbitrary size and alignment. It does this using a fixed-size buffer , which is three pointers in size and pointer-aligned. This either directly contains the value, if its size and alignment are both less than or equal to the fixed-size buffer's, or contains a pointer to a side allocation owned by the existential container. The type of the contained value is identified by its type metadata record, and witness tables for all of the required protocol conformances are included. The layout is as if declared in the following C struct:
struct OpaqueExistentialContainer {
void *fixedSizeBuffer[3];
Metadata *type;
WitnessTable *witnessTables[NUM_WITNESS_TABLES];
};
So an existential container is 40 bytes in size (on a 64-bit platform) of which 24 bytes are available to hold the contained object. If the object doesn't fit into 24 bytes, Swift instead allocates the object on the heap and stores a pointer in the container.
Now, here's a custom view:
import SwiftUI
struct Hello: View {
var body: some View {
Text("Hello!")
}
}
How big is the body of a Hello view?
MemoryLayout<Hello.Body>.size
// 32
The body (a Text) is 32 bytes. In SwiftUI's design (where body returns an associated type), when SwiftUI asks for that body, it allocates those 32 bytes on the stack.
In your design, where body's type is View? and there's no associated type, the body property returns an existential container, which is 40 bytes. SwiftUI allocates those 40 bytes on the stack. But the body getter needs to put a 32-byte object (the Text) into that container, and the container only has room for a 24-byte payload. So the body getter has to allocate 32 bytes on the heap and store the pointer in the container.
Returning an existential container forces heap allocation whenever the object is more than 24 bytes in size. Returning an associated type doesn't force a heap allocation.