ios - Swift - 使用UnsafePointer传递变量和传递变量地址之间的区别?

标签 ios swift

最近我试图查看我的变量的地址,但我有这个问题。

var age: Int = 5

withUnsafePointer(to: age) {
    print($0) // 0x00007ffee3362750
    print($0.pointee) // 5
}

withUnsafePointer(to: &age) {
    print($0) // 0x000000010d226330
    print($0.pointee) // 5
}

为什么它显示不同的内存地址以及为什么它为 pointee 显示相同的值?

var strarr = [1, 2]

withUnsafePointer(to: strarr[0]) {
    print("\($0)") // 0x00007ffee3362750
    print("\($0.pointee)") // 1
}

withUnsafePointer(to: strarr[1]) {
    print("\($0)") // 0x00007ffee3362750
    print("\($0.pointee)") // 2
}

withUnsafePointer(to: &strarr[0]) {
    print("\($0)") // 0x0000600002755760
    print("\($0.pointee)") // 1
}

withUnsafePointer(to: &strarr[1]) {
    print("\($0)") // 0x0000600002755768
    print("\($0.pointee)") // 2
}

对于数组,为什么当我没有传递变量地址时它为索引 1 和 2 显示相同的内存地址,为什么当我传递变量地址时它为索引 1 和 2 显示不同的内存地址内存地址?

感谢您的回答并期待了解这一点

最佳答案

您看到的不同之处在于您使用了 withUnsafePointer 的两个不同重载,我将按照您使用它们的相同顺序列出它们:

func withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

func withUnsafePointer<T, Result>(to value: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

两者的区别在于 inout 用于 value 参数的限定符。

现在,让我们尝试了解幕后发生的事情。

首先,0x00007ffee3362750 看起来像一个栈指针,而 0x000000010d226330 看起来像一个堆指针。堆栈地址从为程序分配的内存的顶部开始,并随着每次函数调用而减少(并在函数返回时增加)。

这表明 withUnsafePointer 的第一个重载从作为参数传递的变量创建了一个临时的可写变量。这是必需的,因为 UnsafePointer 需要一个 inout 引用才能使用,并且常规参数是只读的。

这意味着 withUnsafePointer 的非 inout 重载的实现看起来像这样:

func withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result {
    var value = value // shadow the argument, create a readwrite location
    return try withUnsafePointer(&value, body)
}

因此,第一个调用需要分配一个中间内存位置,这就是为什么您会看到两个不同的地址,因为地址没有指向同一个内存位置。但是,由于两个内存位置都以相同的值开始,因此打印 pointee 会产生相同的输出。


现在,让我们谈谈数组示例。您看到的行为具有相同的原因:堆栈分配。发生的事情是:

  1. 程序开始执行,栈指针的值为P(随机名)
  2. non-inout withUnsafePointer被调用,这是一个函数调用,栈是为函数预留的;堆栈指针为 P - N
  3. withUnsafePointer 创建临时可写变量,并执行
  4. withUnsafePointer 返回,并释放栈内存,此时栈指针回到P
  5. 第二个非inout withUnsafePointer被调用,栈指针回到P - N,但是由于两者之间没有其他函数调用,相同的栈地址被保留给临时可写变量,因此 UnsafePointer 实例具有相同的地址

这里,即使UnsafePointer指向同一个地址,该地址的值也是不同的,分别对应arr[0]arr的值[1].

对于 inout 调用,UnsafePointer 指向数组缓冲区中项目的实际地址。

这也是您可以为非 inout 调用获取不同值的方法:

withUnsafePointer(to: strarr[0]) {
    print("\($0)")
    print("\($0.pointee)")
}

// this adds a nested function call, which also decreases the stack pointer
// resulting in the temporary location not being on the same stack address
func test() {
    withUnsafePointer(to: strarr[1]) {
        print("\($0)")
        print("\($0.pointee)")
    }
}
test()

关于ios - Swift - 使用UnsafePointer传递变量和传递变量地址之间的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64841775/

相关文章:

iphone - NSDate 字符串未显示在 TableViewCell 中

iOS 收据验证作为后端服务

ios - UIPickerView pickerView :titleForRow:rowforComponent method not called

iOS - 以编程方式从位图中提取剪影

ios - 将 Base64 NSString 转换为 UIImage?

ios - 如何使用 swift 使用 JukeBox 播放保管箱音频文件?

iPhone - 加载新 View 和释放的对象

swift - SceneKit:对 SCNNode 应用多次旋转,但保持原始轴不变

swift - 以编程方式添加约束会破坏自动布局约束

ios - 选择创建此页面的方式的困境