UnsafeMutableRawPointer到UnsafeMutablePointer <t>:指针对象上的EXC_BAD_ACCESS

时间:2017-09-19 21:51:36

标签: swift pointers

我试图从使用UnsafeMutablePointer获得的UnsafeMutableRawPointer获取Unmanaged.passUnretained().toOpaque()

class C { var foo = 42, bar = "bar" }
let c = C()

let rawPointer = Unmanaged.passUnretained(c).toOpaque()

let pointer = rawPointer.bindMemory(to: C.self, capacity: 1)
let pointee = pointer.pointee
print(pointee.foo) // EXC_BAD_ACCESS

这里有一些LLDB输出,这对我来说很奇怪,因为pointer中的一切似乎都没问题,直到我要求它pointee

(lldb) frame variable -L c
scalar: (memtest2.C) c = 0x0000000101d00030 {
0x0000000101d00040:   foo = 42
0x0000000101d00048:   bar = "bar"
}
(lldb) frame variable -L rawPointer
0x00000001005e2e08: (UnsafeMutableRawPointer) rawPointer = {
scalar:   _rawValue = 0x0000000101d00030 {
0x0000000101d00040:     foo = 42
0x0000000101d00048:     bar = "bar"
  }
}
(lldb) frame variable -L pointer
0x00000001005e2e10: (UnsafeMutablePointer<memtest2.C>) pointer = 0x0000000101d00030
(lldb) frame variable -L pointer._rawValue
scalar: (memtest2.C) pointer._rawValue = 0x0000000101d00030 {
0x0000000101d00040:   foo = 42
0x0000000101d00048:   bar = "bar"
}
(lldb) frame variable -L pointee
0x00000001005e2e18: (memtest2.C) pointee = 0x00000001005b65d8 {
0x00000001005b65e8:   foo = 140736790071664
0x00000001005b65f0:   bar = ""
}

我还试过assumingMemoryBound(to:)load(as:),或者只是:

let pointer = UnsafePointer<C>(bitPattern: Int(bitPattern: rawPointer))!
print(pointer.pointee.foo) // EXC_BAD_ACCESS

但我总是得到这个EXC_BAD_ACCESS错误。这是怎么回事?

1 个答案:

答案 0 :(得分:0)

我的rawPointer指向c实例数据所在的位置。正如我所料,它不是指向引用的指针。类是引用类型:c的值是类实例数据所在的内存地址。然而,toOpaque()文件很清楚:

  

将非托管类引用不安全地转换为指针。

toOpaque()实际上会调用unsafeBitCast(c, UnsafeMutableRawPointer.self)

要在引用上有一个指针,可以简单地执行:

let referencePointer = UnsafeMutablePointer<C>(&c)

当我的rawPointer指向实例数据时,执行pointer.pointee会告诉运行时实例数据的第一个单词是(或其)引用。这当然不是真的也不合理。

说明:(我稍微更改了我的初始代码:foobar都是Int)

(lldb) frame variable c
(testmem.C) c = 0x0000000101833580 (foo = 42, bar = 84)

这个(0x0000000101833580)是实例数据所在的位置。让我们看看内存中包含的内容:

(lldb) memory read 0x0000000101833580
0x101833580: e0 65 5b 00 01 00 00 00 02 00 00 00 00 00 00 00
0x101833590: 2a 00 00 00 00 00 00 00 54 00 00 00 00 00 00 00

我了解到第一个单词(e0 65 5b 00 01 00 00 00)是类型数据,第二个单词(02 00 00 00 00 00 00 00)是引用计数(我不太了解这个),其余的是实例数据。实际上,0x2a为42,0x54为84. foobar的值。

执行pointer.pointee意味着告诉运行时第一个单词(e0 65 5b 00 01 00 00 000x00000001005b65e0)是指向我们的实例数据所在位置的引用(显然不是这种情况)!暗示pointer.pointee.foo位于0x00000001005b65e0 + 16(0x00000001005b65f0)和bar位于+ 24(0x00000001005b65f0)。

(lldb) memory read 0x00000001005b65e0
0x1005b65e0: a9 65 5b 00 01 80 1d 00 80 62 5b 00 03 00 00 00
0x1005b65f0: 70 e9 10 9e ff 7f 00 00 00 00 00 00 00 00 00 00

foo包含0x0000007fff9e10e970,十进制为140735845296496,对应于:

(lldb) frame variable -L pointee
0x00000001005e2e18: (testmem.C) pointee = 0x00000001005b65e0 {
0x00000001005b65f0:   foo = 140735845296496 // this
0x00000001005b65f8:   bar = 0
}

由于我的程序没有分配这些数据,因此我们无法访问它,因此EXC_BAD_ACCESS错误。

生活现在很有意义。