以下是我使用的示例代码:
class Dummy {
let uuid = UUID()
}
func test() {
let dummy = Dummy()
let unmangedOpaquePointer = Unmanaged.passUnretained(dummy).toOpaque()
let fromWithUnsafeAPIPointer = withUnsafePointer(to: dummy, { UnsafeMutableRawPointer(mutating: $0) })
print(unmangedOpaquePointer == fromWithUnsafeAPIPointer) // false
let dummy1 = Unmanaged<Dummy>.fromOpaque(unmangedOpaquePointer).takeUnretainedValue()
let dummy2 = fromWithUnsafeAPIPointer.assumingMemoryBound(to: Dummy.self).pointee
let dummy3 = Unmanaged<Dummy>.fromOpaque(fromWithUnsafeAPIPointer).takeUnretainedValue()
let dummy4 = unmangedOpaquePointer.assumingMemoryBound(to: Dummy.self).pointee
print(dummy1 === dummy2) // true
print(dummy1 === dummy3) // EXC_BAD_ACCESS
print(dummy2 === dummy4) // EXC_BAD_ACCESS
}
test()
print(unmangedOpaquePointer == fromWithUnsafeAPIPointer) // false
上述代码片段的结果是“false”。这有点令人期待。 unmangedOpaquePointer 和 fromWithUnsafeAPIPointer 是
UnsafeMutableRawPointer
的不同实例,因此它们不相等。
然而,它们在这里都指向同一个对象dummy。如果 unmangedOpaquePointer == fromWithUnsafeAPIPointer
不是实现它的正确方法,我该如何测试这个事实?
Unmanaged
API 返回的 UnsafeMutableRawPointer 和 withUnsafePointer
API 有什么区别?print(dummy1 === dummy2) // true
print
调用表示从两个指针检索到的 dummy1 和 dummy2 是相同的(相同的对象实例)。然而 dummy1 === dummy3
和 dummy2 === dummy4
不是运行时有效的表达式(因为它们都会崩溃)。
因此,通过调用 Unmanaged
API 返回的指针似乎只能与 Unmanaged
API 一起使用来检索指针指向的值。 withUnsafePointer
API 也是如此。为什么?
在@Sweeper的帮助下,我测试了示例代码的另一个版本:
class Dummy {
let uuid = UUID()
}
func test() {
var dummy = Dummy()
let unmangedOpaquePointer = Unmanaged.passUnretained(dummy).toOpaque()
withUnsafeMutablePointer(to: &dummy) {
let fromWithUnsafeAPIPointer = UnsafeMutableRawPointer($0)
print(unmangedOpaquePointer == fromWithUnsafeAPIPointer) // false
let dummy1 = Unmanaged<Dummy>.fromOpaque(unmangedOpaquePointer).takeUnretainedValue()
let dummy2 = fromWithUnsafeAPIPointer.assumingMemoryBound(to: Dummy.self).pointee
let dummy3 = Unmanaged<Dummy>.fromOpaque(fromWithUnsafeAPIPointer).takeUnretainedValue()
let dummy4 = unmangedOpaquePointer.assumingMemoryBound(to: Dummy.self).pointee
print(dummy1 === dummy2) // true
print(dummy1 === dummy3) // EXC_BAD_ACCESS
print(dummy2 === dummy4) // EXC_BAD_ACCESS
}
}
test()
这纠正了使用
withUnsafePointer
API 的错误方式。但结果是一样的。
Dummy
是引用类型。当您执行 var dummy = Dummy()
时,类实例中的实际 UUID 存储在内存中的某个位置(堆),而不是存储在堆栈上的 dummy
变量中。 dummy
只存储对类实例的 reference 打个比方,Swift 类型 Dummy
类似于 C 中的 Dummy *
,其中 Dummy
是一个 C 结构体。请注意,您无法在 Swift 中表达 C 类型 Dummy
。
使用
withUnsafeMutablePointer
获得的指针的类型为 UnsafeMutablePointer<Dummy>
。正如我们所确定的,Dummy
本身只是对堆上类实例的引用,因此UnsafeMutablePointer<Dummy>
是指向堆上类实例引用的指针。用 C 语言来说,这就像一个 Dummy **
。
然而,使用 Unmanaged.toOpaque
获得的原始指针只是指向类实例的指针 - 与
dummy
变量存储的内容相同。这是根本的区别。 使用
fromOpaque
和
takeUnretainedValue
可以有效地将原始指针转换为
Dummy
。这些是不同的 Swift 类型,但它们实际上是同一件事——对类实例的引用。在 C 类比中,这看起来就像将
void *
转换为
Dummy *
。至关重要的是,没有取消引用。在
dummy3
中,您尝试将
Dummy **
转换为
Dummy *
,这显然非常糟糕。但是,使用
pointee
,is 取消引用。同样,使用 C 表示法,您可以取消引用
Dummy **
来获取它所指向的
Dummy *
(就像您在
dummy2
中所做的那样),但您不能取消引用
Dummy *
并期望仍获得
Dummy *
,如
dummy4
。