2015-10-23 10 views
26

Den Versuch, „Selbst“ zu einer C-Funktion in schnellen, wenn Sie anrufen folgenden Code weitergeben muß:Wie werfen selbst UnsafeMutablePointer <Void> Typen in schnellen

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

Was den idealen Weg ist „Selbst“ ein werfen UnsafeMutablePointer hier eingeben?

+0

Wie weisen Sie self zu inputProcRefCon zu? Ich hoffe, Sie haben dies durch- https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html – Shripada

+1

Ein anderes Beispiel: http://stackoverflow.com/questions/33260808/swift- ordnungsgemäße-Nutzung-von-cfnotificationcentererdobserver-w-callback/33262376 # 33262376. –

+0

Eigentlich @MartinR Ich denke deine Antwort hier ist hilfreicher: http://StackOverflow.com/a/30788165/341994 – matt

Antwort

70

einen Objektzeiger (dh eine Instanz eines Referenztyp) kann mit einem UnsafePointer<Void> umgewandelt (dem Swift-Mapping von const void *, UnsafeRawPointer in Swift 3) und zurück. In Objective-C würden Sie

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

(Siehe 3.2.4 Bridged casts in der Clang ARC-Dokumentation für die genaue Bedeutung dieser Würfe.) Schreiben

Swift hat einen Unmanaged Typ für diesen Zweck. Es ist ein wenig umständlich zu verwenden, weil es mit COpaquePointer statt UnsafePointer<Void> arbeitet. Hier sind zwei Hilfsmethoden (benannt nach der 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) // *** 
} 

Der „kompliziert“ Ausdruck ist nur dann notwendig Swifts strenges Typsystem zu erfüllen. In dem kompilierten Code ist dies nur eine Besetzung zwischen Zeigern. (Es kann kürzer geschrieben werden, wie in den *** Kommentaren angezeigt , wenn Sie bereit sind, „unsicher“ Methoden zu verwenden, aber die kompilierte Code ist identisch.)

Mit Hilfe dieser Hilfsmethoden Sie self auf eine C-Funktion übergeben können

let voidPtr = bridge(self) 

(UnsafeMutablePointer<Void>(bridge(self)) oder wenn die C-Funktion ein veränderlicher Zeiger erfordert ), und wandelt es zurück auf einen Objektzeiger - zB in einer Callback-Funktion - als

let mySelf : MyType = bridge(voidPtr) 

Keine Übertragung des Eigentums erfolgt, so dass Sie self existieren, solange der void-Zeiger verwendet wird, muss sicherstellen.


Und der Vollständigkeit halber, würde der Swift-Äquivalent von __bridge_retained und __bridge_transfer von Objective-C

sein
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() das Objekt Zeiger auf einen void-Zeiger wirft und behält das Objekt. bridgeTransfer() konvertiert den Void-Zeiger zurück in einen Objektzeiger und verwendet den Retain-Wert.

Ein Vorteil ist, dass das Objekt zwischen den -Aufrufen nicht freigegeben werden kann, da eine starke Referenz gehalten wird. Der Nachteil ist, dass die Anrufe müssen richtig ausbalanciert sein, und dass es leicht dazu führen kann, behalten Zyklen.


Update für 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() 
} 

Die entsprechenden Änderungen an "unsicheren pointers" sind in

+0

Entschuldigung für die späte Antwort und danke für die Mühe. UnsafeMutablePointer (Unmanaged.passUnready (self) .toOpaque()) ist der Weg, um es zu übergeben. Und schön, diese Bridge-Methoden zu haben, um den Zeiger zu konvertieren. Vielen Dank. –

+1

Danke !! So viel Zeit für dieses Problem verschwendet – marchinram

+0

Kann dieser Ansatz wie folgt verwendet werden: 1. Pass in gehaltene "Box" mit "schwachen" Variable zu "Selbst". 2. Callback bietet optional den Wert zu stark an. 3. Wenn die Zuordnung der Instanz aufgehoben wird und "Box" null enthält, verbraucht der Callback das Behalten der "Box". – Kirsteins

3

Es scheint mir, dass withUnsafeMutablePointer ist für - um einen beliebigen Swift-Zeiger in einen C-Zeiger zu konvertieren. So vermutlich könnten Sie dies tun (ich habe es nicht ausprobiert, aber der Code, den ich getestet habe funktioniert sicher):

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

Beachten Sie, dass Speicherverwaltungsprobleme auftreten werden; "unsicherer veränderbarer Zeiger" bedeutet, dass das Zurückhalten des am Ende des Zeigers befindlichen Daten bis zu _you_ reicht. Daher würde ich denken, dass "var mself = self" durch ein hartnäckiges globales ersetzt werden müsste. – matt

+1

Dies übergibt die Adresse von "selbst" an die C-Funktion, nicht die Adresse des Objekts. Wie Sie sagten, muss "ich selbst" ein globaler sein, den ich ziemlich unelegant finde. - Der Code von http://stackoverflow.com/a/30788165/341994 sieht kompliziert aus, tut aber eigentlich nichts. Es wirft den Objektzeiger "self" einfach auf einen veränderbaren Zeiger.In der Tat kann es vereinfacht werden, um "observer = unsafeAddressOf (self)" zu lassen, der generierte Assemblercode scheint identisch zu sein. –

+1

(Forts.) Der Vorteil des expliziten (komplizierten) Ausdrucks besteht darin, dass er die umgekehrte Konvertierung von einem leeren Zeiger in einen Objektzeiger abgleicht und dass er so geändert werden kann, dass er das Objekt behält. –

0

Diese Antwort nicht als themenspezifische sieht für einen Rückruf als Martin R's answer, aber vielleicht von nutzen sein ...

Sie können in der Regel einen Wert von jeder Art zu einem unsicheren void-Zeiger mit dem & Betreiber übergeben:

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

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

jedoch self passieren, was Sie tun müssen, um so in einem Kontext, wo self veränderbar ist. Das heißt, Sie werden dies in einem mutierenden Methode tun müssen (oder einem init) eines Werttyp, kein Referenztyp:

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

var myFooValue = FooValue() 
myFooValue.bar() 

Wenn Sie einen Referenztyp verwenden möchten, müssen Sie erstellen eine lokale Kopie der Referenz und einen Zeiger auf die übergeben:

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

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

Wie ich zu matt gesagt habe, übergibt dies die Adresse der lokalen Variablen an die Funktion, nicht die Adresse des Objekts, auf das "self" zeigt. Ich bin nicht sicher, ob dies verwendet werden kann, um einen Instanzzeiger an eine C-Funktion zu übergeben und sie im Rückruf wiederherzustellen. Ich könnte mich natürlich irren. –

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