web-dev-qa-db-fra.com

Comment se convertir en UnsafeMutablePointer <Void> tapez swift

Essayer de passer "self" à une fonction C dans Swift, lors de l'appel du code suivant:

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

Quel est le moyen idéal pour caster "self" en un type UnsafeMutablePointer ici?

44
Peter Peng

Un pointeur d'objet (c'est-à-dire une instance d'un type de référence) peut être converti en un UnsafePointer<Void> (Le mappage Swift de const void *, UnsafeRawPointer in Swift 3) and back. Dans Objective-C, vous écririez

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

(Voir .2.4 Castings pontés dans la documentation de Clang ARC pour la signification précise de ces castings.)

Swift a un type Unmanaged à cet effet. Il est un peu lourd à utiliser car il fonctionne avec COpaquePointer au lieu de UnsafePointer<Void>. Voici deux méthodes d'assistance (nommées d'après la distribution 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) // ***
}

L'expression "compliquée" n'est nécessaire que pour satisfaire le système de type strict de Swifts. Dans le code compilé, ce n'est qu'une conversion entre les pointeurs. (Il peut être écrit plus court comme indiqué dans les commentaires *** Si vous êtes prêt à utiliser des méthodes "dangereuses", mais le code compilé est identique.)

En utilisant ces méthodes d'assistance, vous pouvez passer self à une fonction C comme

 let voidPtr = bridge(self)

(ou UnsafeMutablePointer<Void>(bridge(self)) si la fonction C nécessite un pointeur mutable), et la reconvertir en pointeur objet - par ex. dans une fonction de rappel - comme

 let mySelf : MyType = bridge(voidPtr)

Aucun transfert de propriété n'a lieu, vous devez donc vous assurer que self existe tant que le pointeur void est utilisé.


Et par souci d'exhaustivité, l'équivalent Swift de __bridge_retained Et __bridge_transfer D'Objective-C serait

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() convertit le pointeur d'objet en un pointeur vide et conserve l'objet. bridgeTransfer() reconvertit le pointeur void en un pointeur objet et consomme la retenue.

Un avantage est que l'objet ne peut pas être désalloué entre les appels car une référence forte est conservée. L'inconvénient est que les appels doivent être correctement équilibrés et qu'ils peuvent facilement provoquer des cycles de rétention.


Mise à jour pour 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()
}

Les modifications pertinentes apportées aux "pointeurs dangereux" sont décrites dans

91
Martin R

Il me semble que c'est à cela que sert withUnsafeMutablePointer - pour convertir un pointeur arbitraire Swift en un pointeur C. Donc, je suppose que vous pourriez le faire (je ne l'ai pas essayé, mais le code que j'ai testé fonctionne en toute sécurité):

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

Cette réponse ne semble pas aussi spécifique au sujet d'un rappel que réponse de Martin R , mais pourrait être utile ...

Vous pouvez généralement transmettre une valeur de n'importe quel type à un pointeur vide dangereux en utilisant le & opérateur:

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

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

Cependant, pour passer self, vous devrez le faire dans un contexte où self est modifiable. Cela signifie que vous devrez le faire dans une méthode de mutation (ou un init) d'un type de valeur, pas un type de référence:

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

var myFooValue = FooValue()
myFooValue.bar()

Si vous souhaitez utiliser un type de référence, vous devrez créer une copie locale de la référence et lui passer un pointeur:

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

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