Hoe u zichzelf cast naar UnsafeMutablePointer<Void> typ snel

Proberen “zelf” snel door te geven aan een C-functie, bij het aanroepen van de volgende code:

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

Wat is hier de ideale manier om “zelf” naar een UnsafeMutablePointer-type te casten?


Antwoord 1, autoriteit 100%

Een objectaanwijzer (d.w.z. een instantie van een referentietype) kan
geconverteerd naar een UnsafePointer<Void>(de Swift-toewijzing van const void *, UnsafeRawPointerin Swift 3) en terug. In Objective-C zou je schrijven

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

(Zie 3.2.4 Bridged castsin de Clang ARC-documentatie voor de precieze betekenis van deze
afgietsels.)

Swift heeft voor dat doel een type Unmanaged.
Het is een beetje omslachtig om te gebruiken omdat het werkt met COpaquePointer
in plaats van UnsafePointer<Void>. Hier zijn twee hulpmethoden:
(vernoemd naar de Objective-C __bridgecast):

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

De “ingewikkelde” uitdrukking is alleen nodig om Swifts tevreden te stellen
strikt type systeem. In de gecompileerde code is dit slechts een cast
tussen wijzers. (Het kan korter worden geschreven zoals aangegeven in de ***opmerkingen
als u bereid bent om “onveilige” methoden te gebruiken, maar de gecompileerde
code is identiek.)

Met behulp van deze hulpmethoden kunt u selfdoorgeven aan een C-functie als

let voidPtr = bridge(self)

(of UnsafeMutablePointer<Void>(bridge(self))als de C-functie vereist
een veranderlijke aanwijzer), en converteer deze terug naar een objectaanwijzer – e.g.
in een callback-functie – als

let mySelf : MyType = bridge(voidPtr)

Er vindt geen eigendomsoverdracht plaats, dus u moet ervoor zorgen dat self
bestaat zolang de lege aanwijzer wordt gebruikt.


En voor de volledigheid: het Swift-equivalent van __bridge_retaineden __bridge_transfervan Objective-C zou zijn

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()werpt de objectaanwijzer naar een lege aanwijzer en
behoudt het voorwerp. bridgeTransfer()converteert de
void pointer terug naar een object pointer en verbruikt de behouden.

Een voordeel is dat het object niet kan worden ongedaan gemaakt tussen de
oproepen omdat er een sterke referentie wordt vastgehouden. Het nadeel is dat
de oproepen moeten goed gebalanceerd zijn en dat dit gemakkelijk kan leiden tot behouden
cycli.


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

De relevante wijzigingen in “onveilige pointers” worden beschreven in


Antwoord 2, autoriteit 4%

Het lijkt mij dat dit is waar withUnsafeMutablePointervoor is – om een ​​willekeurige Swift-pointer om te zetten in een C-pointer. Dus vermoedelijk zou je dit kunnen doen (ik heb het niet geprobeerd, maar de code die ik heb getest werkt veilig):

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

Antwoord 3, autoriteit 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()}

Antwoord 4

Dit antwoord lijkt niet zo onderwerpspecifiek voor een callback als Het antwoord van Martin R, maar kan van pas komen…

U kunt doorgaans een waarde van elk type doorgeven aan een onveilige lege aanwijzer met de operator &:

func baz(p: UnsafeMutablePointer<Void>) -> String {
    return "\(p)"
}
var num = 5
print(baz(&num))

Om selfdoor te geven, moet je dit echter doen in een context waarin selfveranderlijk is. Dat betekent dat je dit moet doen in een muterende methode (of een init) van een waardetype, niet een referentietype:

struct FooValue {
    mutating func bar() {
        print(baz(&self))
    }
}
var myFooValue = FooValue()
myFooValue.bar()

Als je een referentietype wilt gebruiken, moet je een lokale kopie van de referentie maken en daar een verwijzing naar doorgeven:

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

Other episodes