Swift是否保证类和结构中字段的存储顺序?

在C中,您在结构中定义字段的顺序是它们将在内存中实例化的顺序.考虑到内存对齐,下面的结构在内存中的大小为8字节,如图所示,但如果字段反转则只有6个字节,因为不需要任何对齐填充.
struct s {
    int32_t a;
    /* 2 bytes of padding to align a 64 bit integer */
    int64_t b;
}

这种排序保证存在于C结构,C类(和结构)和Objective-C类中.

对于Swift类和结构中的字段,存储顺序是否同样有保证?或者(鉴于该语言不支持与列出的其他语言相同的指针),编译器是否在编译时为您重新安排它们?

是的,内存中struct元素的顺序是
他们的声明.细节可以找到
The Swift ABI
(重点补充).但请注意使用“当前”,所以这个
可能会在未来的Swift版本中发生变化:

Fragile Struct and Tuple Layout

Structs and tuples currently share the same layout algorithm,noted as the “Universal” layout algorithm in the compiler implementation. The algorithm is as follows:

  • Start with a size of 0 and an alignment of 1.
  • Iterate through the
    fields,in element order for tuples,or in var declaration order for
    structs. For each field:
    • Update size by rounding up to the alignment
      of the field,that is,increasing it to the least value greater or
      equal to size and evenly divisible by the alignment of the field.
    • Assign the offset of the field to the current value of size.
    • Update
      size by adding the size of the field.
    • Update alignment to the max of
      alignment and the alignment of the field.
  • The final size and alignment
    are the size and alignment of the aggregate. The stride of the type is
    the final size rounded up to alignment.

填充/对齐与C不同:

Note that this differs from C or LLVM’s normal layout rules in that size and stride are distinct; whereas C layout requires that an embedded struct’s size be padded out to its alignment and that nothing be laid out there,Swift layout allows an outer struct to lay out fields in the inner struct’s tail padding,alignment permitting.

只有从C导入结构,才能保证具有
相同的内存布局.来自Apple的Joe Groff写道
[swift-users] Mapping C semantics to Swift

If you depend on a specific layout,you should define the struct in C and import it into Swift for now.

later in that discussion

You can leave the struct defined in C and import it into Swift. Swift will respect C’s layout.

例:

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,12
print(sizeof(B),strideof(B)) // 10,12

// Swift 3:
print(MemoryLayout<A>.size,MemoryLayout<A>.stride) // 9,12
print(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,12
print(sizeof(CB),strideof(CB)) // 13,16

// Swift 3:
print(MemoryLayout<CA>.size,MemoryLayout<CA>.stride) // 12,12
print(MemoryLayout<CB>.size,MemoryLayout<CB>.stride) // 16,16

因为uint8_t d是在结构CA sa的尾部填充之后布局的.

从Swift 3开始,尺寸和步幅都返回相同的值
从C导入的结构(包括struct padding),
即与C中的sizeof相同的值将返回.

这是一个简单的函数,有助于演示上面的内容(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,c: 0xcc)
showMemory(&ca)   // <aa000000 bbbbbbbb cc000000>

var cb = CB(ca: ca,d: 0xdd)
showMemory(&cb)   // <aa000000 bbbbbbbb cc000000 dd000000>

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...