51

在调用以下代码时,尝试将“self”快速传递给 C 函数:

var callbackStruct : AURenderCallbackStruct = 
    AURenderCallbackStruct.init(
      inputProc: recordingCallback,
      inputProcRefCon: UnsafeMutablePointer<Void>
    )

在这里将“self”转换为 UnsafeMutablePointer 类型的理想方法是什么?

4

4 回答 4

106

对象指针(即引用类型的实例)可以转换为(在 Swift 3 中UnsafePointer<Void>的 Swift 映射)并返回。在 Objective-C 中你会写const void *UnsafeRawPointer

void *voidPtr = (__bridge void*)self;
// 
MyType *mySelf = (__bridge MyType *)voidPtr;

(有关这些转换的确切含义,请参阅 Clang ARC 文档中的3.2.4 桥接转换。)

Swift 有一个Unmanaged用于此目的的类型。使用起来有点麻烦,因为它与COpaquePointer 而不是UnsafePointer<Void>. 这里有两个辅助方法(以 Objective-C 转换命名__bridge):

func bridge<T : AnyObject>(obj : T) -> UnsafePointer<Void> {
    return UnsafePointer(Unmanaged.passUnretained(obj).toOpaque())
    // return unsafeAddressOf(obj) // ***
}

func bridge<T : AnyObject>(ptr : UnsafePointer<Void>) -> T {
    return Unmanaged<T>.fromOpaque(COpaquePointer(ptr)).takeUnretainedValue()
    // return unsafeBitCast(ptr, T.self) // ***
}

“复杂”的表达式只需要满足 Swift 的严格类型系统。在编译的代码中,这只是指针之间的转换。***(如果您愿意使用“不安全”的方法,它可以写得更短,如注释中所示,但编译后的代码是相同的。)

使用此辅助方法,您可以传递self给 C 函数

 let voidPtr = bridge(self)

(或者UnsafeMutablePointer<Void>(bridge(self))如果 C 函数需要一个可变指针),并将其转换回对象指针 - 例如在回调函数中 - 如

 let mySelf : MyType = bridge(voidPtr)

不会发生所有权转移,因此self 只要使用 void 指针,您就必须确保它存在。


为了完整起见,Objective-C 的 Swift 等价物__bridge_retained__bridge_transfer

func bridgeRetained<T : AnyObject>(obj : T) -> UnsafePointer<Void> {
    return UnsafePointer(Unmanaged.passRetained(obj).toOpaque())
}

func bridgeTransfer<T : AnyObject>(ptr : UnsafePointer<Void>) -> T {
    return Unmanaged<T>.fromOpaque(COpaquePointer(ptr)).takeRetainedValue()
}

bridgeRetained()将对象指针转换为 void 指针并保留该对象。bridgeTransfer()将 void 指针转换回对象指针并使用保留。

一个优点是不能在调用之间释放对象,因为持有强引用。缺点是调用必须适当平衡,并且很容易导致保留循环。


Swift 3 (Xcode 8) 的更新:

func bridge<T : AnyObject>(obj : T) -> UnsafeRawPointer {
    return UnsafeRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}

func bridge<T : AnyObject>(ptr : UnsafeRawPointer) -> T {
    return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
}

func bridgeRetained<T : AnyObject>(obj : T) -> UnsafeRawPointer {
    return UnsafeRawPointer(Unmanaged.passRetained(obj).toOpaque())
}

func bridgeTransfer<T : AnyObject>(ptr : UnsafeRawPointer) -> T {
    return Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()
}

对“不安全指针”的相关更改在

于 2015-10-23T19:26:53.463 回答
4

在我看来,这就是withUnsafeMutablePointer- 将任意 Swift 指针转换为 C 指针。所以大概你可以这样做(我没有尝试过,但我测试过的代码可以安全地工作):

var mself = self 
withUnsafeMutablePointer(&mself) { v in
    let v2 = UnsafeMutablePointer<Void>(v)
    myStruct.inputProcRefCon = v2
}
于 2015-10-23T15:24:21.967 回答
2
func bridge<T : AnyObject>(obj : T) -> UnsafePointer<Void> {
return UnsafePointer(Unmanaged.passUnretained(obj).toOpaque())
}


func bridge<T : AnyObject>(ptr : UnsafePointer<Void>) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
}

func bridgeRetained<T : AnyObject>(obj : T) -> UnsafePointer<Void> {
return UnsafePointer( Unmanaged.passRetained(obj).toOpaque())}

func bridgeTransfer<T : AnyObject>(ptr : UnsafePointer<Void>) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()}
于 2016-07-07T11:05:24.740 回答
0

这个答案看起来不像Martin R 的答案那样特定于回调的主题,但可能有用......

您通常可以使用&运算符将​​任何类型的值传递给不安全的 void 指针:

func baz(p: UnsafeMutablePointer<Void>) -> String {
    return "\(p)"
}

var num = 5
print(baz(&num))

但是,要通过self,您需要在self可变的上下文中这样做。这意味着您需要在init值类型的变异方法(或 )中执行此操作,而不是引用类型:

struct FooValue {
    mutating func bar() {
        print(baz(&self))
    }
}

var myFooValue = FooValue()
myFooValue.bar()

如果要使用引用类型,则需要创建引用的本地副本并将指针传递给它:

class FooReference {
    func bar() {
        var localSelf = self
        print(baz(&localSelf))
    }
}

let myFooReference = FooReference()
myFooReference.bar()
于 2015-10-23T17:15:33.300 回答