2015-10-23 21 views
26

Cercando di passare "sé" a una funzione C a Swift, al momento della chiamata seguente codice:Come lanciare auto a UnsafeMutablePointer <Void> tipo in rapida

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

Qual è il modo ideale per lanciare "auto" ad un UnsafeMutablePointer type qui?

+0

Come si assegna auto a inputProcRefCon? Spero che tu abbia approfondito questo argomento: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html – Shripada

+1

Un altro esempio: http://stackoverflow.com/questions/33260808/swift- corretto uso-di-cfnotificationcenteraddobserver-w-callback/33262376 # 33262376. –

+0

In realtà @MartinR Penso che la tua risposta qui sia più utile: http://stackoverflow.com/a/30788165/341994 – matt

risposta

70

un puntatore di oggetto (ossia un'istanza di un tipo riferimento) può essere convertito UnsafePointer<Void> (mappatura Swift di const void *, UnsafeRawPointer in Swift 3) e ritorno. In Objective-C si può scrivere

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

(Cfr 3.2.4 Bridged casts nella documentazione Clang ARC per il preciso significato di queste calchi.)

Swift ha un tipo di Unmanaged a tale scopo. È un po 'ingombrante da utilizzare perché funziona con COpaquePointer anziché UnsafePointer<Void>. Qui ci sono due metodi di supporto (dal nome del __bridge getto Objective-C):

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) // *** 
} 

L'espressione "complicata" è solo necessario per conformarsi a Rondoni rigoroso sistema di tipo. Nel codice compilato questo è solo un cast tra i puntatori. (Può essere scritto più breve, come indicato nelle *** commenti se si è disposti a usare metodi "non sicuri", ma il codice compilato è identica.)

L'utilizzo di questo metodi di supporto è possibile passare self a una funzione C come

let voidPtr = bridge(self) 

(o UnsafeMutablePointer<Void>(bridge(self)) se la funzione C richiede un puntatore mutevole), e convertirlo indietro ad un puntatore oggetto - es in una funzione di callback - come

let mySelf : MyType = bridge(voidPtr) 

Nessun trasferimento di proprietà avviene, quindi è necessario assicurarsi che self esiste fino a quando il puntatore nullo viene utilizzato.


E per amor di completezza, l'equivalente di Swift __bridge_retained e __bridge_transfer da Objective-C sarebbe

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() getta il puntatore all'oggetto a un puntatore nullo e conserva l'oggetto. bridgeTransfer() converte il puntatore del vuoto di in un puntatore a un oggetto e utilizza il retain.

Un vantaggio è che l'oggetto non può essere deallocato tra le chiamate perché viene mantenuto un riferimento forte. Lo svantaggio è che le chiamate devono essere correttamente bilanciate e che può facilmente causare mantenere cicli .


Aggiornamento per 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() 
} 

I cambiamenti relativi alla "puntatori non sicuri" sono descritti in

+0

Ci scusiamo per la risposta tardiva e grazie per lo sforzo. UnsafeMutablePointer (Unmanaged.passUnretained (self) .toOpaque()) è il modo per superarlo. E bello avere quei metodi bridge per convertire il puntatore. Grazie. –

+1

Grazie !! Ho perso così tanto tempo su questo problema – marchinram

+0

Questo approccio può essere usato in questo modo: 1. Passare in 'Box' mantenuto con la variabile' weak' su 'self'. 2. La callback opzionalmente ordina il valore su strong. 3. Se l'istanza è deallocata e 'Box' contiene nil, il callback consuma il mantenimento della' Scatola'? – Kirsteins

3

Mi sembra che questo sia quello che è withUnsafeMutablePointer per convertire un puntatore Swift arbitrario in un puntatore C. Quindi, presumibilmente si potrebbe fare questo (non ho provato, ma il codice che ho provato funziona in modo sicuro):

var mself = self 
withUnsafeMutablePointer(&mself) { v in 
    let v2 = UnsafeMutablePointer<Void>(v) 
    myStruct.inputProcRefCon = v2 
} 
+0

Si noti che ci saranno problemi di gestione della memoria; "puntatore mutabile non sicuro" significa che mantenere ciò che si trova all'estremità del puntatore è fino a _you_. Quindi penserei che 'var mself = self' debba essere sostituito da un persistente globale. – matt

+1

Questo passa l'indirizzo di 'mself' alla funzione C, non l'indirizzo dell'oggetto. Come hai detto, 'mself' deve essere un globale che trovo abbastanza poco elegante. - Il codice da http://stackoverflow.com/a/30788165/341994 sembra complicato, ma in realtà non fa * nulla. Semplicemente getta il puntatore dell'oggetto 'self' su un puntatore mutevole.Infatti può essere semplificato in 'let observer = unsafeAddressOf (self)', il codice assembly generato sembra essere identico. –

+1

(seguito) Il vantaggio dell'espressione esplicita (complicata) è che corrisponde alla conversione inversa dal puntatore del vuoto al puntatore dell'oggetto e che può essere modificato per conservare l'oggetto. –

0

Questa risposta non sembra-argomento specifico per un callback come Martin R's answer, ma potrebbe essere utile ...

in genere è possibile passare un valore di qualsiasi tipo a un puntatore nullo non sicuro utilizzando il & dell'operatore:

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

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

Tuttavia, per passare self avrete bisogno di farlo in un contesto in cui self è modificabile. Questo significa che avrete bisogno di fare questo in un metodo mutante (o un init) di un tipo di valore, non è un tipo di riferimento:

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

var myFooValue = FooValue() 
myFooValue.bar() 

Se si desidera utilizzare un tipo di riferimento, è necessario creare una copia locale del riferimento e passare un puntatore che:

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

let myFooReference = FooReference() 
myFooReference.bar() 
+0

Come ho detto a matt, questo passa l'indirizzo della variabile locale alla funzione, non l'indirizzo dell'oggetto puntato da 'self'. Non sono sicuro se questo può essere utilizzato per passare un puntatore di istanza a una funzione C e recuperarlo nel callback. Potrei sbagliarmi, naturalmente. –

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