Cómo lanzarse al tipo UnsafeMutablePointer<Void> en swift
pointers casting (4)
Intentando pasar "self" a una función C de forma rápida, al llamar al siguiente código:
var callbackStruct : AURenderCallbackStruct =
AURenderCallbackStruct.init(
inputProc: recordingCallback,
inputProcRefCon: UnsafeMutablePointer<Void>
)
¿Cuál es la forma ideal de emitir "self" a un tipo UnsafeMutablePointer aquí?
Me parece que para eso es con
withUnsafeMutablePointer
: para convertir un puntero Swift arbitrario en un puntero C.
Entonces, presumiblemente, podría hacer esto (no lo he probado, pero el código que he probado funciona de manera segura):
var mself = self
withUnsafeMutablePointer(&mself) { v in
let v2 = UnsafeMutablePointer<Void>(v)
myStruct.inputProcRefCon = v2
}
Un puntero de objeto (es decir, una instancia de un
tipo de referencia
) puede convertirse en un
UnsafePointer<Void>
(el mapeo Swift de
const void *
,
UnsafeRawPointer
en Swift 3) y viceversa.
En Objective-C escribirías
void *voidPtr = (__bridge void*)self;
//
MyType *mySelf = (__bridge MyType *)voidPtr;
(Ver 3.2.4 Moldes en puente en la documentación de Clang ARC para el significado exacto de estos moldes).
Swift tiene un tipo no
Unmanaged
para ese propósito.
Es un poco engorroso de usar porque funciona con
COpaquePointer
lugar de
UnsafePointer<Void>
.
Aquí hay dos métodos auxiliares (nombrados después del reparto de 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) // ***
}
La expresión "complicada" solo es necesaria para satisfacer el sistema de tipo estricto de Swifts.
En el código compilado, esto es solo un reparto entre punteros.
(Se puede escribir más corto como se indica en los comentarios
***
si está dispuesto a utilizar métodos "inseguros", pero el código compilado es idéntico).
Usando estos métodos de ayuda puede pasar a una función C como
let voidPtr = bridge(self)
(o
UnsafeMutablePointer<Void>(bridge(self))
si la función C requiere un puntero mutable) y conviértalo de nuevo a un puntero de objeto, por ejemplo, en una función de devolución de llamada, como
let mySelf : MyType = bridge(voidPtr)
No se realiza ninguna transferencia de propiedad, por lo que debe asegurarse de que existe
self
mismo siempre que se utilice el puntero nulo.
Y en aras de la exhaustividad, el equivalente rápido de
__bridge_retained
y
__bridge_transfer
de Objective-C sería
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()
el puntero del objeto en un puntero vacío y retiene el objeto.
bridgeTransfer()
convierte el puntero vacío de nuevo en un puntero de objeto y consume la retención.
Una ventaja es que el objeto no se puede desasignar entre las llamadas porque se mantiene una referencia fuerte. La desventaja es que las llamadas deben estar correctamente equilibradas, y que puede causar fácilmente ciclos de retención.
Actualización para 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()
}
Los cambios relevantes a los "punteros inseguros" se describen en
Esta respuesta no parece tan específica del tema para una devolución de llamada como la respuesta de Martin R , pero podría ser útil ...
Por lo general, puede pasar un valor de cualquier tipo a un puntero vacío inseguro utilizando el operador
&
:
func baz(p: UnsafeMutablePointer<Void>) -> String {
return "/(p)"
}
var num = 5
print(baz(&num))
Sin embargo, para aprobar el
self
, deberá hacerlo en un contexto en el que el
self
sea mutable.
Eso significa que tendrá que hacer esto en un método de mutación (o un
init
) de un tipo de valor, no un tipo de referencia:
struct FooValue {
mutating func bar() {
print(baz(&self))
}
}
var myFooValue = FooValue()
myFooValue.bar()
Si desea utilizar un tipo de referencia, deberá crear una copia local de la referencia y pasar un puntero a eso:
class FooReference {
func bar() {
var localSelf = self
print(baz(&localSelf))
}
}
let myFooReference = FooReference()
myFooReference.bar()
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()}