Swift知识一览(三)

原创文章
声明:作者声明此文章为原创,未经作者同意,请勿转载,若转载,务必注明本站出处,本平台保留追究侵权法律责任的权利。
全栈老韩
全栈工程师,擅长iOS App开发、前端(vue、react、nuxt、小程序&Taro)开发、Flutter、React Native、后端(midwayjs、golang、express、koa)开发、docker容器、seo优化等。

一、枚举的内存布局

1.1 内存分配

从上一章内容《Swift知识一览(二)》https://www.xinwei.ltd/article/103 中,我们知道可以通过MemoryLayout来查看枚举类型所占的字节数。如下例子:

swift 复制代码
enum Direction {
    case east, south, west, north
}
// 打印该枚举类型所占内存
print(MemoryLayout.size(ofValue: Direction.east)) // 1,解释:1个字节可以存储该枚举类型

// 上述枚举类型,在实际的内存存储中,从east开始存储0,1,2,3
  • 原始值不影响枚举类型的内存存储大小的。

每一个枚举成员都共用1个字节,其实针对上面的枚举案例或者有原始值的枚举,针对每一个case的枚举变量,存储的都是0、1、2、3这样的排序数字。原始值类型枚举不影响枚举成员内存大小。

  • 关联值,内存大小是最长的那个关联case长度 + 1字节的case识别。其他case枚举成员都共用关联类型最长的那个枚举成员的内存空间。

举例如下:

swift 复制代码
enum CustomNumber {
    case one(Int, Int, Int)
    case two(Int, Int)
    case three(Int)
    case four(Bool)
    case other
}

// 类似上面这种枚举,内存是:
size: 3 * 8 + 1 = 25 // 因为最大关联类型的参数最多有3个,足够存储其他关联类型。1是用来表示存储第几个case
// 查看内存分布的时候,可以看到1字节中存储的是从对应于枚举成员排序的0、1、2、3、4这几个值
alignment: 8
stride: 32

// 只有1个关联类型参数的情况
enum CustomEnum {
    case one(Int)
}
// size、stride、aligment应该都是8(64位平台)。无须存储识别case枚举成员的那1个字节。

1.2 Swift中获取变量内存地址

使用withUnsafePointer来获取变量内存地址。
函数定义:

swift 复制代码
/// Invokes the given closure with a pointer to the given argument.
///
/// The `withUnsafePointer(to:_:)` function is useful for calling Objective-C
/// APIs that take in parameters by const pointer.
///
/// The pointer argument to `body` is valid only during the execution of
/// `withUnsafePointer(to:_:)`. Do not store or return the pointer for later
/// use.
///
/// - Parameters:
///   - value: An instance to temporarily use via pointer. Note that the `inout`
///     exclusivity rules mean that, like any other `inout` argument, `value`
///     cannot be directly accessed by other code for the duration of `body`.
///     Access must only occur through the pointer argument to `body` until
///     `body` returns.
///   - body: A closure that takes a pointer to `value` as its sole argument. If
///     the closure has a return value, that value is also used as the return
///     value of the `withUnsafePointer(to:_:)` function. The pointer argument
///     is valid only for the duration of the function's execution.
///     It is undefined behavior to try to mutate through the pointer argument
///     by converting it to `UnsafeMutablePointer` or any other mutable pointer
///     type. If you need to mutate the argument through the pointer, use
///     `withUnsafeMutablePointer(to:_:)` instead.
/// - Returns: The return value, if any, of the `body` closure.
@inlinable public func withUnsafePointer<T, Result>(to value: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

使用:

swift 复制代码
var dir = Direction.east

let result = withUnsafePointer(to: &dir) { address in
    return address
}
print("--0--dir内存地址--\(result)")

1.3 枚举内存分配汇编举证

  • 第一步,如下图所示,红色框内是枚举,按照图中设置断点。通过withUnsafePointer获取变量dir的内存地址,然后右键控制台左侧的dir变量,点击View Memory。
    查看swift中枚举内存1.jpg

  • 第二步,如下图所示,会显示内存查看界面,但是在第1步点击后进来时,Address输入框是空值,Xcode没有允许我们访问枚举变量的内存地址。所以我们需要输入控制台打印出来的内存地址,才能查看内存。
    查看swift中枚举内存2.jpg

  • 第三步,进入下一个断点,查看给dir赋值.south这个枚举成员后,dir会存储什么值。(按照1.1中的解释,应该存储1)
    查看swift中枚举内存3.jpg

同样跟第二步一样,查看View Memory。这一步可以验证,dir此时存储的是1.
查看swift中枚举内存4.jpg

  • 第四步,重复第三步的操作,进入到下一个断点。这个时候,dir被赋值了枚举成员west。按照1.1中的解释,此时dir中应该存储的是2.
    查看swift中枚举内存5.jpg
    查看swift中枚举内存6.jpg

这是《Swift知识一览》系列的第三篇文章,若感兴趣,请关注后续发表的文章。

暂无评论,快来发表第一条评论吧