是的,结构元素在内存中的顺序就是它们声明的顺序。详细信息可以在“
类型布局”
(已添加重点)中找到。但是请注意使用“当前”,因此在以后的Swift版本中可能会发生变化:
脆弱的结构和元组布局
结构和元组 当前 共享相同的布局算法,在编译器实现中称为“通用”布局算法。算法如下:
- 以0的大小和1的对齐方式开始。
- 以元组的元素顺序或结构的 var声明顺序 遍历字段。对于每个字段:
- 通过四舍五入到字段的对齐方式来更新大小,即将其增大到大于或等于大小的最小值,并且可以被字段的对齐方式整除。
- 将字段的偏移量分配给size的当前值。
- 通过添加字段的大小来更新大小。
- 将对齐方式更新为最大对齐方式和字段的对齐方式。
- 最终大小和对齐方式是聚合的大小和对齐方式。该类型的跨度是将最终大小四舍五入以对齐。
填充/对齐方式与C不同:
请注意,这与C或LLVM的常规布局规则不同,因为大小和跨度是不同的。C布局要求将嵌入式结构的大小填充为其对齐方式,并且在那里不进行任何布局,而Swift布局允许外部结构在内部结构的尾部填充中布局字段,并允许对齐。
仅当从C 导入 结构时,才能保证它具有相同的内存布局。Apple的Joe Groff在[swift-
users]上写道:将
C语义映射到Swift
如果您依赖于特定的布局,则应该在C中定义该结构并将其导入到Swift中。
而后来在讨论:
您可以保留在C中定义的结构并将其导入Swift。 Swift将尊重C的布局。
例:
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, 12print(sizeof(B), strideof(B)) // 10, 12// Swift 3:print(MemoryLayout<A>.size, MemoryLayout<A>.stride) // 9, 12print(MemoryLayout<B>.size, MemoryLayout<B>.stride) // 10, 12这
var d: UInt8是在的尾部填充中布置的
var sa: A。如果您在C中定义相同的结构
struct CA { uint8_t a; uint32_t b; uint8_t c;};struct CB { struct CA ca; uint8_t d;};然后将其导入到Swift
// Swift 2:print(sizeof(CA), strideof(CA)) // 9, 12print(sizeof(CB), strideof(CB)) // 13, 16// Swift 3:print(MemoryLayout<CA>.size, MemoryLayout<CA>.stride) // 12, 12print(MemoryLayout<CB>.size, MemoryLayout<CB>.stride) // 16, 16
因为
uint8_t d是在尾巴填充后布局的
struct CA sa。
从Swift 3开始,对于 从C导入 的结构,两者
size和都
stride返回相同的值( 包括 结构填充) ,
即
sizeof返回与C中相同的值。
这是一个简单的函数,有助于演示以上内容(Swift 3):
func showMemory<T>(_ ptr: UnsafePointer<T>) { let data = Data(bytes: UnsafeRawPointer(ptr), count: MemoryLayout<T>.size) print(data as NSData)}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>
从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>



