wake-up-neo.com

Wie Sie sich in UnsafeMutablePointer <Void> umwandeln, geben Sie swift

Der Versuch, "self" an eine C-Funktion in Swift zu übergeben, wenn der folgende Code aufgerufen wird:

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

Was ist der ideale Weg, um "self" in einen UnsafeMutablePointer-Typ umzuwandeln?

44
Peter Peng

Ein Objektzeiger (dh eine Instanz eines Referenztyps) kann in ein UnsafePointer<Void> (Die Swift Zuordnung von const void *) Konvertiert werden. UnsafeRawPointer in Swift 3) und zurück. In Objective-C würden Sie schreiben

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

(Siehe .2.4 Überbrückte Casts in der Clang ARC-Dokumentation für die genaue Bedeutung dieser Casts.)

Swift hat zu diesem Zweck einen Unmanaged -Typ. Die Verwendung ist etwas umständlich, da sie mit COpaquePointer anstelle von UnsafePointer<Void> Funktioniert. Hier sind zwei Hilfsmethoden (benannt nach dem Cast 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) // ***
}

Der "komplizierte" Ausdruck ist nur notwendig, um das strenge Typensystem von Swifts zu erfüllen. Im kompilierten Code ist dies nur eine Umwandlung zwischen Zeigern. (Es kann, wie in den Kommentaren *** Angegeben, kürzer geschrieben werden, wenn Sie bereit sind, "unsichere" Methoden zu verwenden, der kompilierte Code jedoch identisch ist.)

Mit diesen Hilfsmethoden können Sie self an eine C-Funktion übergeben als

 let voidPtr = bridge(self)

(oder UnsafeMutablePointer<Void>(bridge(self)), wenn die C-Funktion einen veränderlichen Zeiger erfordert), und konvertieren Sie ihn zurück in einen Objektzeiger - z. in einer Rückruffunktion - als

 let mySelf : MyType = bridge(voidPtr)

Es findet keine Eigentumsübertragung statt. Sie müssen also sicherstellen, dass self vorhanden ist, solange der Stornozeiger verwendet wird.


Der Vollständigkeit halber wäre das Swift Äquivalent von __bridge_retained Und __bridge_transfer Aus Objective-C

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() wandelt den Objektzeiger in einen leeren Zeiger und behält das Objekt bei. bridgeTransfer() konvertiert den leeren Zeiger zurück in einen Objektzeiger und verbraucht den Retain.

Ein Vorteil ist, dass das Objekt nicht zwischen den Aufrufen freigegeben werden kann, da eine starke Referenz vorhanden ist. Der Nachteil ist, dass die Anrufe richtig ausgeglichen werden müssen und dass es leicht zu Haltezyklen kommen kann.


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 relevanten Änderungen an "unsicheren Zeigern" sind in beschrieben

91
Martin R

Es scheint mir, dass dies das ist, wofür withUnsafeMutablePointer ist - um einen beliebigen Swift Zeiger in einen C Zeiger umzuwandeln. Vermutlich könnten Sie dies tun (ich habe es nicht versucht, aber der Code, den ich getestet habe, funktioniert sicher):

var mself = self 
withUnsafeMutablePointer(&mself) { v in
    let v2 = UnsafeMutablePointer<Void>(v)
    myStruct.inputProcRefCon = v2
}
4
matt
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()}
2
Mike

Diese Antwort ist für einen Rückruf nicht so themenspezifisch wie Antwort von Martin R , kann aber von Nutzen sein ...

Normalerweise können Sie einen Wert eines beliebigen Typs an einen unsicheren Leerenzeiger übergeben, indem Sie & Operator:

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

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

Um jedoch self zu übergeben, müssen Sie dies in einem Kontext tun, in dem self veränderbar ist. Das bedeutet, dass Sie dies in einer mutierenden Methode (oder einem init) eines Werttyps und nicht eines Referenztyps tun müssen:

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

var myFooValue = FooValue()
myFooValue.bar()

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

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

let myFooReference = FooReference()
myFooReference.bar()
0
Nate Cook