非托管 API 和 withUnsafePointer API 之间的区别?

问题描述 投票:0回答:1

以下是我使用的示例代码:

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()
  • 不同的 UnsafeMutableRawPointer 实例
print(unmangedOpaquePointer == fromWithUnsafeAPIPointer) // false

上述代码片段的结果是“false”。这有点令人期待。 unmangedOpaquePointer 和 fromWithUnsafeAPIPointer 是

UnsafeMutableRawPointer
的不同实例,因此它们不相等。 然而,它们在这里都指向同一个对象dummy。如果
unmangedOpaquePointer == fromWithUnsafeAPIPointer
不是实现它的正确方法,我该如何测试这个事实?

  • API 调用应该配对吗?为什么?从
    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 的错误方式。但结果是一样的。

ios swift pointers
1个回答
0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.