问题
Trying to pass "self" to a C function in swift, when calling following code:
var callbackStruct : AURenderCallbackStruct =
AURenderCallbackStruct.init(
inputProc: recordingCallback,
inputProcRefCon: UnsafeMutablePointer<Void>
)
What is the ideal way to cast "self" to a UnsafeMutablePointer type here?
回答1:
An object pointer (i.e. an instance of a reference type) can be
converted to a UnsafePointer<Void>
(the Swift mapping of const void *
, UnsafeRawPointer
in Swift 3) and back. In Objective-C you would write
void *voidPtr = (__bridge void*)self;
//
MyType *mySelf = (__bridge MyType *)voidPtr;
(See 3.2.4 Bridged casts in the Clang ARC documentation for the precise meaning of these casts.)
Swift has an Unmanaged
type for that purpose.
It is a bit cumbersome to use because it works with COpaquePointer
instead of UnsafePointer<Void>
. Here are two helper methods
(named after the Objective-C __bridge
cast):
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) // ***
}
The "complicated" expression is only necessary to satisfy Swifts
strict type system. In the compiled code this is just a cast
between pointers. (It can be written shorter as indicated in the ***
comments
if you are willing to use "unsafe" methods, but the compiled
code is identical.)
Using this helper methods you can pass self
to a C function as
let voidPtr = bridge(self)
(or UnsafeMutablePointer<Void>(bridge(self))
if the C function requires
a mutable pointer), and convert it back to an object pointer – e.g.
in a callback function – as
let mySelf : MyType = bridge(voidPtr)
No transfer of ownership takes place, so you must ensure that self
exists as long as the void pointer is used.
And for the sake of completeness, the Swift equivalent of __bridge_retained
and __bridge_transfer
from Objective-C would be
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()
casts the object pointer to a void pointer and
retains the object. bridgeTransfer()
converts the
void pointer back to an object pointer and consumes the retain.
An advantage is that the object cannot be deallocated between the calls because a strong reference is held. The disadvantage is that the calls must be properly balanced, and that it can easily cause retain cycles.
Update for 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()
}
The relevant changes to "unsafe pointers" are described in
- SE-0017 Change Unmanaged to use UnsafePointer
- SE-0107 UnsafeRawPointer API
回答2:
It seems to me that this is what withUnsafeMutablePointer
is for - to convert an arbitrary Swift pointer into a C pointer. So presumably you could do this (I have not tried it, but the code I've tested works safely):
var mself = self
withUnsafeMutablePointer(&mself) { v in
let v2 = UnsafeMutablePointer<Void>(v)
myStruct.inputProcRefCon = v2
}
回答3:
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()}
回答4:
This answer doesn't look as topic-specific for a callback as Martin R's answer, but might be of use...
You can typically pass a value of any type to an unsafe void pointer using the &
operator:
func baz(p: UnsafeMutablePointer<Void>) -> String {
return "\(p)"
}
var num = 5
print(baz(&num))
However, to pass self
you'll need to do so in a context where self
is mutable. That means you'll need to do this in a mutating method (or an init
) of a value type, not a reference type:
struct FooValue {
mutating func bar() {
print(baz(&self))
}
}
var myFooValue = FooValue()
myFooValue.bar()
If you want to use a reference type, you'll need to create a local copy of the reference and pass a pointer to that:
class FooReference {
func bar() {
var localSelf = self
print(baz(&localSelf))
}
}
let myFooReference = FooReference()
myFooReference.bar()
来源:https://stackoverflow.com/questions/59785859/how-do-you-get-the-memory-address-of-the-class-data-not-the-variable-that-point