Unlock Swift’s Memory Secrets: How HandyJSON Bypasses Runtime for Faster JSON Parsing
This article explores Swift's memory model and pointer mechanics, explains how HandyJSON converts JSON to class instances without runtime support, demonstrates MemoryLayout utilities, and shows practical techniques for modifying struct and class properties directly in memory.
HandyJSON is an open‑source Swift library for converting JSON data into class or struct instances, similar to JSONModel . Because Swift lacks Objective‑C‑style runtime, HandyJSON bypasses runtime by directly manipulating instance memory to assign property values.
Memory Allocation
Stack: stores temporary value‑type variables and function call frames.
Heap: stores reference‑type instances.
MemoryLayout
Basic Usage
MemoryLayoutwas introduced in Swift 3.0 to calculate the size of a type in memory.
MemoryLayout<Int>.size // 8
let a: Int = 10
MemoryLayout.size(ofValue: a) // 8MemoryLayout Properties
MemoryLayoutprovides three useful Int properties:
alignment & alignment(ofValue: T)
This reflects the memory‑alignment requirement; on a 64‑bit system the maximum alignment is 8 bytes.
size & size(ofValue: T)
The number of contiguous bytes occupied by an instance of T.
stride & stride(ofValue: T)
The distance between successive elements in an array of T, accounting for alignment.
Basic Data Types MemoryLayout
// Value types
MemoryLayout<Int>.size // 8
MemoryLayout<Int>.alignment // 8
MemoryLayout<Int>.stride // 8
MemoryLayout<String>.size // 24
MemoryLayout<String>.alignment // 8
MemoryLayout<String>.stride // 24
// Reference type T
MemoryLayout<T>.size // 8
MemoryLayout<T>.alignment // 8
MemoryLayout<T>.stride // 8
// Pointer types
MemoryLayout<UnsafeMutablePointer<T>>.size // 8
MemoryLayout<UnsafeMutablePointer<T>>.alignment // 8
MemoryLayout<UnsafeMutablePointer<T>>.stride // 8
MemoryLayout<UnsafeMutableBufferPointer<T>>.size // 16
MemoryLayout<UnsafeMutableBufferPointer<T>>.alignment // 16
MemoryLayout<UnsafeMutableBufferPointer<T>>.stride // 16Swift Pointers
Common Swift Pointer Types
unsafePointer<T> ≈ const T *
Getting a Pointer to an Object
final func withUnsafeMutablePointers<R>(_ body: (UnsafeMutablePointer<Header>, UnsafeMutablePointer<Element>) throws -> R) rethrows -> R
var a: T = T()
let aPointer = a.withUnsafeMutablePointer { return $0 }
func headPointerOfStruct() -> UnsafeMutablePointer<Int8> {
return withUnsafeMutablePointer(to: &self) {
return UnsafeMutableRawPointer($0).bindMemory(to: Int8.self, capacity: MemoryLayout<Self>.stride)
}
}
func headPointerOfClass() -> UnsafeMutablePointer<Int8> {
let opaque = Unmanaged.passUnretained(self as AnyObject).toOpaque()
let typed = opaque.bindMemory(to: Int8.self, capacity: MemoryLayout<Self>.stride)
return UnsafeMutablePointer<Int8>(typed)
}Struct Memory Model
Structs are value types stored on the stack.
struct Point {
var a: Double
var b: Double
}
MemoryLayout<Point>.size // 16When a property becomes optional, the struct size increases due to alignment padding.
struct Point {
var a: Double?
var b: Double
}
MemoryLayout<Point>.size // 24
MemoryLayout<Double>.size // 8
MemoryLayout<Optional<Double>>.size // 9Modifying a Struct Property via Memory
let animal = Animal()
let animalPtr: UnsafeMutablePointer<Int8> = animal.headPointerOfStruct()
let animalRawPtr = UnsafeMutableRawPointer(animalPtr)
let aPtr = animalRawPtr.advanced(by: 0).assumingMemoryBound(to: Int.self)
aPtr.pointee // 1
aPtr.initialize(to: 100)
aPtr.pointee // 100Class Memory Model
Classes are reference types stored on the heap; the stack holds a pointer to the heap instance. The heap also stores type metadata and ARC reference count.
class Human {
var age: Int?
var name: String?
var nicknames: [String] = []
func headPointerOfClass() -> UnsafeMutablePointer<Int8> {
let opaque = Unmanaged.passUnretained(self as AnyObject).toOpaque()
let typed = opaque.bindMemory(to: Int8.self, capacity: MemoryLayout<Human>.stride)
return UnsafeMutablePointer<Int8>(typed)
}
}
MemoryLayout<Human>.size // 8Modifying a Class Property via Memory
let human = Human()
let humanRawPtr = UnsafeMutableRawPointer(human.headPointerOfClass())
let nickPtr = humanRawPtr.advanced(by: 64).assumingMemoryBound(to: Array<String>.self)
nickPtr.initialize(["goudan","zhaosi","wangwu"])Understanding V‑Table Dispatch
Swift uses a type pointer in each class instance that points to static type metadata containing a V‑Table. Method calls are resolved by looking up the method address in this table.
Swapping a Class’s Type Pointer
By overwriting the type pointer of an instance, you can make a Fox object behave like a Wolf object, causing method dispatch to invoke the wrong implementation.
let wolf = Wolf()
let fox = Fox()
let wolfPtr = UnsafeMutableRawPointer(wolf.headPointerOfClass())
let foxPtr = UnsafeMutableRawPointer(fox.headPointerOfClass())
foxPtr.advanced(by: 0).bindMemory(to: UnsafeMutablePointer<Wolf.Type>.self, capacity: 1).initialize(to: wolfPtr.advanced(by: 0).assumingMemoryBound(to: UnsafeMutablePointer<Wolf.Type>.self).pointee)
print(type(of: fox)) // Wolf
fox.soul() // "my soul is wolf"These experiments demonstrate how Swift’s memory layout and type metadata can be inspected and manipulated at runtime.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
