Swift: sizeof与MemoryLayout

sizeof

C语言中,可能会经常与sizeof打交道,用来计算数据占用内存大小。在Csizeof即可以作用于类型也可以作用于某个实际的变量,并返回其在内存中的尺寸size_t

Swift 3以前,也有sizeof,不过与C中的运算符不同,它经过了一层包装,变成了一个只接受类型的方法,而接受具体值的则为另一个方法: sizeofValue

1
2
func sizeof<T>(_: T.Type) -> Int
func sizeofValue<T>(_: T) -> Int

不过sizeofValue接受的虽然是具体值,但是返回的是这个值的实际大小,而不是其内容的大小。所以与C中用sizeof拿来计算数组内容在内存中占据的尺寸不一样:

1
2
3
4
5
6
// C
char bytes[] = {1, 2, 3};
sizeof(bytes); // 3

int bytes[] = {1, 2, 3};
sizeof(bytes); // 12
1
2
3
// Swift
let bytes = [1, 2, 3]
sizeofValue(bytes) // 8: 64位系统一个引用的长度

MemoryLayout

Swift 3sizeofsizeofValue相关的被另一个新的枚举所替代,那就是MemoryLayout

基本使用方法

1
2
3
let a = 10
MemoryLayout<Int>.size // 8
MemoryLayout.size(ofValue: a) // 8

属性方法介绍

MemoryLayout有3个非常有用的属性及3个对应的Value方法,返回值都为Int类型。

size

实例使用size(ofValue: T)

T占用连续内存的大小,单位是字节。类型的大小不包括任何动态分配或不合适的存储,当T是类类型时,MemoryLayout<T>.size是相同的,不论T中存储属性有多少。当使用unsafe pointerT的多个实例分配内存时,使用多个该类型的stride而不是其size,这个涉及到内存对齐的问题,详见stride

stride

实例使用stride(ofValue: T)

当存储在连续存储器或Array<T>中时,从T的任意一个实例开始到下一个实例开始所占用的连续内存字节大小。

示例:

FA7434C3-B586-4E6D-BCA7-EEE42460C837

这是一个数组,里面有四个T类型元素,每个T元素的大小为size个字节,但是因为内存对齐的限制,每个T类型元素实际消耗的内存空间为stride个字节,stride - size个字节则为每个元素因为内存对齐而浪费的内存空间。

alignment

实例使用alignment(ofValue: T)

T的默认内存对齐方式,单位为字节。许多计算机系统对基本数据类型的合法地址做出了一些限制,要求某种数据类型对象的地址必须是某个值K(通常是 2、4或者8)的倍数。这种对齐限制简化了形成处理器和内存系统之间接口的硬件设计。对齐原则是任何K字节的基本对象的地址必须是K的倍数。

MemoryLayout.alignment就代表着数据类型T的内存对齐原则。而且在64位系统下,最大的内存对齐原则是8字节。

基本数据类型的MemoryLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 值类型
MemoryLayout<Int>.size // 8
MemoryLayout<Int>.stride // 8
MemoryLayout<Int>.alignment // 8

MemoryLayout<String>.size // 24
MemoryLayout<String>.stride // 24
MemoryLayout<String>.alignment // 8

// 引用类型 T
MemoryLayout<T>.size // 8
MemoryLayout<T>.stride // 8
MemoryLayout<T>.alignment // 8

// 指针类型
MemoryLayout<unsafeMutablePointer<T>>.size // 8
MemoryLayout<unsafeMutablePointer<T>>.stride // 8
MemoryLayout<unsafeMutablePointer<T>>.alignment // 8

MemoryLayout<unsafeMutableBufferPointer<T>>.size // 16
MemoryLayout<unsafeMutableBufferPointer<T>>.stride // 16
MemoryLayout<unsafeMutableBufferPointer<T>>.alignment // 16

注意

以上所有都在64位系统中得到

参考资料