You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unmanaged与withUnsafePointer API的差异及指针使用问题

Swift中Unmanaged与withUnsafePointer指针的差异问题

原始测试代码

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()

问题1:验证指针指向同一对象的方法

执行以下代码输出为false:

print(unmangedOpaquePointer == fromWithUnsafeAPIPointer) // false

两个UnsafeMutableRawPointer实例本身不相等,但它们实际指向同一个dummy对象。既然无法通过指针相等性验证这一点,该如何操作?

问题2:API配对使用的原因及指针差异

代码中dummy1 === dummy2输出true,说明从两个指针获取的是同一实例,但dummy1 === dummy3和dummy2 === dummy4会触发EXC_BAD_ACCESS崩溃。看起来Unmanaged系列API返回的指针只能搭配UnmanagedAPI使用,withUnsafePointer系列API返回的指针同理,请问这是为什么?

修正withUnsafePointer用法后的测试代码

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的错误用法后,执行结果与原始代码一致。


问题解答

针对问题1的解决方案

要验证两个指针是否指向同一对象,不能直接比较指针本身的相等性,而是要通过指针指向的对象实例来判断:

  • 对于Unmanaged获取的指针,用takeUnretainedValue()取出实例;
  • 对于withUnsafePointer获取的指针,用assumingMemoryBound(to:).pointee取出实例;
  • 最后用===比较两个实例是否为同一引用,就像代码中dummy1 === dummy2那样,结果为true就说明指针指向同一对象。

另外也可以通过比较对象的唯一标识(比如示例中的uuid)来辅助验证:

print(dummy1.uuid == dummy2.uuid) // true

针对问题2的原因解析

这两个API返回的指针本质上指向的内存位置完全不同:

  1. Unmanaged.passUnretained(_:).toOpaque():返回的是Swift对象的实际内存地址(即对象的引用本身指向的地址)。Unmanaged的设计目的是处理与C/ObjC交互时的对象引用,它直接操作的是对象的底层引用,所以必须用Unmanaged.fromOpaque(_:).takeUnretainedValue()来还原——这个方法知道如何从对象的实际地址中正确取出实例。

  2. withUnsafePointer(to:):返回的是变量dummy本身的内存地址,而不是变量指向的对象的地址。变量dummy是一个存储了对象引用的栈变量,所以这个指针指向的是栈上存储引用的位置,而不是堆上的对象实例。当你用assumingMemoryBound(to: Dummy.self).pointee时,Swift会从这个栈地址中读取存储的引用,进而拿到对象实例;但如果用Unmanaged.fromOpaque(_:)处理这个指针,它会错误地把栈地址当成对象的实际地址去解析,自然会触发内存访问错误——因为栈地址根本不是Swift对象的有效内存区域。

简单来说:

  • Unmanaged的指针指向堆上的对象实例;
  • withUnsafePointer的指针指向栈上的变量(存储对象引用的容器);
    二者指向的内存区域完全不同,所以必须用对应的API来解析,跨API使用就会访问错误的内存地址,导致崩溃。

内容的提问来源于stack exchange,提问作者Jiang Wang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 12:44:59