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

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

上述代码片段的结果为"假".这是意料之中的.unmangedOpaquePointer和fromWithUnsafeAPIPointer是UnsafeMutableRawPointer的不同实例,因此它们不相等. 然而,它们在这里都指向相同的对象dummy.如果unmangedOpaquePointer == fromWithUnsafeAPIPointer不是实现这个事实的正确方法,我该如何测试这个事实呢?

  • API调用应该配对吗?为什么?从Unmanaged个API返回的UnsafeMutableRawPointer和withUnsafePointer个API之间有什么区别?
print(dummy1 === dummy2) // true

print调用表明从两个指针检索到的dummy 1和dummy 2相同(相同的对象实例).然而,dummy1 === dummy3dummy2 === 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()时,类实例中的实际UID存储在内存(堆)中的某个地方,而不是存储在堆栈上的dummy变量中.dummy仅将reference存储到类实例作为类比,Swift类型Dummy类似于C中的Dummy *,其中Dummy是C struct .请注意,您无法在Swift中表达C类型Dummy.

您用withUnsafeMutablePointer获得的指针类型为UnsafeMutablePointer<Dummy>.正如我们所确定的那样,Dummy本身只是对堆上类实例的引用,因此UnsafeMutablePointer<Dummy>是指向堆上类实例的引用的指针.用C术语来说,这就像Dummy **一样.

然而,您用Unmanaged.toOpaque获得的原始指针只是指向类实例的指针-与dummy变量存储的内容相同.This is the fundamental difference.

这解释了为什么两个原始指针不相等.他们指出了截然不同的事情.

使用fromOpaquetakeUnretainedValue有效地将原始指针转换为Dummy.这些是不同的Swift类型,但它们实际上是一样的--对类实例的引用.在C类比中,这看起来就像将void *铸造到Dummy *.至关重要的是,不存在解除引用的情况.在dummy3中,您试图将Dummy **转换为Dummy *,显然效果非常糟糕.

然而,使用pointee,则使用isgo 引用.同样,使用C符号,您可以取消引用Dummy **以获得它所指向的Dummy *(就像在dummy2中所做的那样),但您不能取消引用Dummy *并期望仍然获得Dummy *,就像在dummy4中一样.

在表格中总结这一点:

Swift Analogous to C
Dummy Dummy *
UnsafeMutablePointer<Dummy> Dummy **
UnsafeMutableRawPointer void *
fromOpaque then takeUnretained Casting to Dummy *
assumingMemoryBound(to: Dummy.self) then pointee Casting to Dummy ** then dereference

这里有一些类似的C伪代码来说明dummy3dummy4的问题.

Dummy *dummy = ...;

// dummy is a pointing to a Dummy
void *unmangedOpaquePointer = dummy;

// &dummy is a Dummy **, pointing to a Dummy *
void *fromWithUnsafeAPIPointer = &dummy; 

// OK, unmangedOpaquePointer is indeed pointing to a Dummy
Dummy *dummy1 = (Dummy *)unmangedOpaquePointer;

// OK, fromWithUnsafeAPIPointer is a Dummy **, pointing to a Dummy* and we get the Dummy * it is pointing to
Dummy *dummy2 = *(Dummy **)fromWithUnsafeAPIPointer;

// BAD! fromWithUnsafeAPIPointer is not a Dummy *
Dummy *dummy3 = (Dummy *)fromWithUnsafeAPIPointer;

// BAD! unmangedOpaquePointer is not a Dummy **
Dummy *dummy4 = *(Dummy **)unmangedOpaquePointer;

Ios相关问答推荐

Flutter应用程序无法使用IOS/Swift的蓝牙核心库发现某些外围设备

在Android和iOS上从后台恢复应用程序后,inappwebview上出现白屏?

与iPadOS中带有扣件的模式相似的组件是什么?

在SwiftUI中动态隐藏列表的空部分

如何在 Swift 中存储具有关联值的协议?

当 .searchable 修饰符处于活动状态时,如何将变量设置为 false?

将 Riverpod 从 StateNotifier 修复为 NotifierProvider 以及应用程序生命周期监控

iOS上从谷歌云存储API不断收到网络连接已丢失的错误信息

在Xcode 15中为具有@Binding的视图创建SwiftUI #预览的方法

如何解决错误 HE0004:无法在 Visual Studio 2022 中加载框架ContentDeliveryServices

无法从 Xcode 获取模拟器列表.请打开 Xcode 并try 直接从那里运行项目以解决剩余问题

视觉扫描无法识别 iOS16 上的 GS1 条码特殊字符

NSOperation 有延迟 - 它是异步的还是同步的?

有哪些可靠的机制可以防止 CoreData CloudKit 中的数据重复?

Xcode 8,iOS 10 -为进程启动 WebFilter 日志(log)记录

不变违规:应用程序 AwesomeProject 尚未注册使用静态 jsbundle 为 iOS 设备构建时

呈现和关闭模态视图控制器

swift 语言中的 null / nil

iOS7 UITextView contentsize.height 替代

如何关闭 iOS 键盘?