Agent skill
memory
Swift 6.2 InlineArray and Span types for zero-overhead memory access, fixed-size collections, and safe pointer alternatives. Use when optimizing performance-critical code paths.
Install this agent skill to your Project
npx add-skill https://github.com/rshankras/claude-code-apple-skills/tree/main/skills/swift/memory
SKILL.md
InlineArray and Span
Guidance for Swift 6.2's low-level memory types: InlineArray for fixed-size inline storage without heap allocation, and Span for safe, zero-cost access to contiguous memory. These replace common uses of UnsafeBufferPointer and hand-tuned tuple storage with compiler-checked alternatives.
When This Skill Activates
Use this skill when the user:
- Asks about InlineArray, fixed-size arrays, or stack-allocated collections
- Mentions Span, MutableSpan, RawSpan, or UTF8Span
- Wants to eliminate heap allocations in hot paths
- Is replacing UnsafeBufferPointer or UnsafePointer with safe alternatives
- Asks about value generics or
let count: Intgeneric parameters - Needs zero-copy access to collection storage
- Mentions inline storage, contiguous memory, or memory layout
- Is doing binary parsing, signal processing, or embedded Swift work
- Wants to avoid copy-on-write overhead for small fixed collections
- Asks about non-escapable types or lifetime dependencies in Swift
Decision Tree
What memory optimization do you need?
│
├─ A fixed-size collection that never grows/shrinks
│ │
│ ├─ Size known at compile time, stored on stack
│ │ └─ InlineArray<N, Element>
│ │ ├─ No heap allocation
│ │ ├─ No reference counting
│ │ └─ No copy-on-write (eager copies)
│ │
│ └─ Size may vary at runtime
│ └─ Array<Element> (standard library)
│
├─ Safe read access to contiguous memory
│ │
│ ├─ Read-only access to typed elements
│ │ └─ Span<Element>
│ │
│ ├─ Mutable access to typed elements
│ │ └─ MutableSpan<Element>
│ │
│ ├─ Read-only access to raw bytes
│ │ └─ RawSpan
│ │
│ ├─ Mutable access to raw bytes
│ │ └─ MutableRawSpan
│ │
│ ├─ Unicode text processing
│ │ └─ UTF8Span
│ │
│ └─ Initializing a new collection's storage
│ └─ OutputSpan
│
├─ Unsafe pointer access (legacy or interop)
│ └─ UnsafeBufferPointer / UnsafeMutableBufferPointer
│ └─ Prefer Span instead for new code
│
└─ Standard dynamic collection
└─ Array<Element>
├─ Heap-allocated, copy-on-write
└─ Grows/shrinks dynamically
API Availability
| API | Minimum Version | Notes |
|---|---|---|
InlineArray<let count: Int, Element> |
Swift 6.2 | Uses value generics; @frozen struct |
Span<Element> |
Swift 6.2 | Non-escapable, lifetime-dependent |
MutableSpan<Element> |
Swift 6.2 | Mutable variant of Span |
RawSpan |
Swift 6.2 | Untyped byte-level access |
MutableRawSpan |
Swift 6.2 | Mutable untyped byte access |
UTF8Span |
Swift 6.2 | Unicode-aware text processing |
OutputSpan |
Swift 6.2 | For initializing collection storage |
.span property on Array |
Swift 6.2 | Returns Span<Element> |
.span property on Data |
Swift 6.2 | Returns Span<UInt8> |
Top 5 Mistakes
| # | Mistake | Fix |
|---|---|---|
| 1 | Trying to append/remove elements on InlineArray |
InlineArray is fixed-size; use Array if you need dynamic sizing |
| 2 | Returning a Span from a function |
Span is non-escapable and cannot outlive its source; restructure to process data within the same scope |
| 3 | Capturing a Span in a closure |
Span cannot be captured; pass the span as a parameter or use Array for escaped contexts |
| 4 | Using InlineArray for large or frequently copied collections |
InlineArray copies eagerly (no COW); use Array for large data that is shared or copied often |
| 5 | Accessing a Span after mutating the source container |
Mutation invalidates the span; re-acquire the span after any modification |
InlineArray
Declaration
InlineArray uses Swift's value generics to encode the count in the type:
@frozen struct InlineArray<let count: Int, Element> where Element: ~Copyable
Initialization
// Explicit count
let a: InlineArray<4, Int> = [1, 2, 4, 8]
// Count inferred from literal
let b: InlineArray<_, Int> = [1, 2, 4, 8] // count = 4
// Element type inferred from literal
let c: InlineArray<4, _> = [1, 2, 4, 8] // Element = Int
// Both inferred
let d: InlineArray = [1, 2, 4, 8] // InlineArray<4, Int>
Memory Layout
Elements are stored contiguously with no overhead. Size equals count * MemoryLayout<Element>.stride:
MemoryLayout<InlineArray<0, UInt16>>.size // 0
MemoryLayout<InlineArray<0, UInt16>>.stride // 1
MemoryLayout<InlineArray<3, UInt16>>.size // 6 (2 bytes x 3)
MemoryLayout<InlineArray<3, UInt16>>.stride // 6
MemoryLayout<InlineArray<3, UInt16>>.alignment // 2 (same as UInt16)
Basic Usage
var array: InlineArray<3, Int> = [1, 2, 3]
// Subscript access
array[0] = 4
// Iterate via indices
for i in array.indices {
print(array[i])
}
// Copies are eager (no copy-on-write)
var copy = array
copy[0] = 99
// array[0] is still 4
InlineArray vs Array
| Characteristic | InlineArray | Array |
|---|---|---|
| Storage | Inline (stack or enclosing type) | Heap-allocated buffer |
| Size | Fixed at compile time | Dynamic |
| Copy semantics | Eager (full copy) | Copy-on-write |
| Reference counting | None | Yes (buffer reference) |
| Exclusivity checks | None | Yes |
| Append/remove | Not supported | Supported |
Span Family
Span (Read-Only)
Provides safe, direct access to contiguous typed memory. Non-escapable -- cannot outlive the source.
let array = [1, 2, 3, 4]
let span = array.span // Span<Int>
// Access elements
let first = span[0]
let count = span.count
// Iterate
for element in span {
print(element)
}
MutableSpan
Mutable access to contiguous storage:
var array = [1, 2, 3, 4]
array.withMutableSpan { span in
for i in span.indices {
span[i] *= 2
}
}
// array is now [2, 4, 6, 8]
RawSpan
Untyped byte-level access for binary data:
let data = Data([0xFF, 0x00, 0xAB, 0xCD])
let rawSpan = data.span // Span<UInt8> for byte-level access
UTF8Span
Specialized for safe and efficient Unicode processing of UTF-8 encoded text.
Safety Constraints
Non-Escapable: Cannot Return from Functions
Span has a lifetime dependency on the container it was derived from. It cannot be returned:
// ❌ Wrong -- Span cannot escape the function
func getSpan() -> Span<UInt8> {
let array: [UInt8] = Array(repeating: 0, count: 128)
return array.span // Compiler error
}
// ✅ Right -- process within the same scope
func processData(_ array: [UInt8]) {
let span = array.span
// Use span here
let sum = span.reduce(0, +)
}
Non-Escapable: Cannot Capture in Closures
// ❌ Wrong -- Span cannot be captured
func makeClosure() -> () -> Int {
let array: [UInt8] = Array(repeating: 0, count: 128)
let span = array.span
return { span.count } // Compiler error
}
// ✅ Right -- use Span within the local scope only
func countElements(_ array: [UInt8]) -> Int {
let span = array.span
return span.count
}
Invalidation on Mutation
Modifying the source container invalidates any existing span:
// ❌ Wrong -- span is invalidated after mutation
var array = [1, 2, 3]
let span = array.span
array.append(4) // Invalidates span
// let x = span[0] // Undefined behavior
// ✅ Right -- re-acquire span after mutation
var array = [1, 2, 3]
array.append(4)
let span = array.span // Fresh span after mutation
let x = span[0] // Safe
Performance Considerations
When InlineArray Wins
- Small, fixed-size data (e.g., color components, matrix rows, coordinate tuples)
- Hot loops where heap allocation / ARC overhead is measurable
- Embedded Swift or contexts with no allocator
- Struct fields that should be stored inline rather than behind a pointer
When Array Wins
- Collection size is unknown or variable
- Collection is large and frequently shared (COW avoids full copies)
- Collection is passed across module boundaries where dynamic sizing is needed
When Span Wins Over UnsafePointer
- All new code that needs contiguous memory access -- Span provides the same performance with compile-time safety
- Binary parsing where you read structured data from a byte buffer
- Algorithm implementations that operate on slices of memory
When UnsafePointer Is Still Needed
- C interop requiring raw pointer parameters
- Legacy APIs that predate Span adoption
- Interfacing with system calls that take pointer arguments
When to Use / When NOT to Use
Use InlineArray When
- You know the exact element count at compile time
- You need zero heap allocation (stack storage)
- The collection is small (< ~64 elements as a guideline)
- You modify in place but rarely copy the whole collection
- You are writing performance-critical or embedded code
Do NOT Use InlineArray When
- The collection size varies at runtime
- The collection is large and frequently copied (no COW means expensive copies)
- You need to append, insert, or remove elements
- You benefit from sharing storage across variables
Use Span When
- You need fast, safe, read-only access to contiguous memory
- You want to replace
UnsafeBufferPointerwith a safe alternative - You are processing data in place without needing to escape the reference
- You are implementing high-performance algorithms on collection storage
Do NOT Use Span When
- You need to store the reference for later use (Span is non-escapable)
- You need to pass memory access across async boundaries
- You need mutable access (use
MutableSpaninstead) - C interop requires actual
UnsafePointerarguments
Review Checklist
InlineArray
- Count is a compile-time constant (value generic), not a runtime variable
- Collection size is genuinely fixed and will not need dynamic resizing
- No code attempts to
append,insert, orremoveelements - Copies are intentional -- each assignment copies all elements eagerly
- Large
InlineArrayvalues are not copied in hot loops (pass byinoutor reference) -
MemoryLayoutis verified if precise byte layout matters (e.g., GPU buffers, file formats)
Span
- Span is never returned from a function or stored in a property
- Span is never captured in an escaping closure
- Source container is not mutated while a span is in use
- Span is re-acquired after any mutation to the source
- Correct span variant is used:
Span(read),MutableSpan(write),RawSpan(bytes) -
UnsafeBufferPointeris only used where C interop requires it; preferSpanotherwise
General Performance
- Profile before optimizing -- confirm heap allocation or ARC is the actual bottleneck
- Small fixed collections use
InlineArrayinstead ofArrayin measured hot paths -
Spanis used instead ofUnsafeBufferPointerfor safe contiguous access - No premature optimization --
Arrayis correct default for most code
References
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
legal
Legal document generation and compliance guidance for indie Apple developers. Covers privacy policies, terms of service, EULAs, GDPR/CCPA/DPDP compliance, and Apple App Store legal requirements. Use when user needs legal documents or compliance guidance.
privacy-policy
Generate privacy policies, terms of service, and EULAs for Apple platform apps. Detects data collection patterns, third-party SDKs, and generates region-specific legal documents with Apple Privacy Nutrition Label mapping. Use when user needs legal documents or data collection disclosure for App Store submission.
ios-development
Comprehensive iOS development guidance including Swift best practices, SwiftUI patterns, UI/UX review against HIG, and app planning. Use for iOS code review, best practices, accessibility audits, or planning new iOS apps.
assistive-access
Assistive Access implementation for cognitive accessibility including simplified scenes, navigation icons, runtime detection, and design principles. Use when optimizing apps for Assistive Access mode.
ui-review
Review SwiftUI code for iOS/watchOS Human Interface Guidelines compliance, font usage, Dynamic Type support, and accessibility. Use when user mentions UI review, HIG, accessibility audit, font checks, or wants to verify interface design against Apple standards.
navigation-patterns
SwiftUI navigation architecture patterns including NavigationStack, NavigationSplitView, TabView, programmatic navigation, and custom transitions. Use when reviewing or building navigation, fixing navigation bugs, or architecting app flow.
Didn't find tool you were looking for?