UnsafeMutableRawPointer 到 UnsafeMutablePointer<T>:指针上的 EXC_BAD_ACCESS

Posted

技术标签:

【中文标题】UnsafeMutableRawPointer 到 UnsafeMutablePointer<T>:指针上的 EXC_BAD_ACCESS【英文标题】:UnsafeMutableRawPointer to UnsafeMutablePointer<T>: EXC_BAD_ACCESS on pointee 【发布时间】:2017-09-19 21:51:35 【问题描述】:

我正在尝试从使用Unmanaged.passUnretained().toOpaque() 获得的UnsafeMutableRawPointer 中获取UnsafeMutablePointer

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 错误。这是怎么回事?

【问题讨论】:

试试let obj = Unmanaged&lt;C&gt;.fromOpaque(rawPointer).takeUnretainedValue();print(obj.foo) @OOPer 我的最终目标不是访问C 实例,而是真正从原始实例中获取有效的类型化指针。 那我无法理解你的最终目标。无论如何,只有当rawPointer 是指向C 实例的引用的指针时,您的代码才有效,Unmanaged.passUnretained(c).toOpaque() 不成功。 let obj = Unmanaged&lt;C&gt;.fromOpaque(rawPointer).takeUnretainedValue() is 是指向 C 实例的有效类型指针,是您问题的正确答案。比较***.com/questions/33294620/…。 @MartinR takeUnretainedValue() 在我想获得Unsafe[Mutable]Pointer&lt;C&gt; 实例时返回c 实例(来自UnsafeMutableRawPointer)。 【参考方案1】:

我的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 错误。

现在的生活变得有意义了?。

【讨论】:

以上是关于UnsafeMutableRawPointer 到 UnsafeMutablePointer<T>:指针上的 EXC_BAD_ACCESS的主要内容,如果未能解决你的问题,请参考以下文章

Swift 结构警告“'UnsafeMutableRawPointer' 的初始化导致指针悬空”

如何使用 UnsafeMutableRawPointer 填充数组?

无法分配类型“UnsafeMutableRawPointer!”的值输入“UIView”

收到“UnsafeMutableRawPointer”类型的错误消息没有下标成员

取消引用 UnsafeMutablePointer<UnsafeMutableRawPointer>

将 UnsafeMutableRawPointer 作为函数参数传递