web-dev-qa-db-ja.com

自分自身をUnsafeMutablePointer <Void>型にswift

次のコードを呼び出すときに、SwiftのC関数に「self」を渡そうとします。

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

ここで「自己」をUnsafeMutablePointer型にキャストする理想的な方法は何ですか?

44
Peter Peng

オブジェクトポインター(つまり、参照型のインスタンス)は、Swift内のUnsafePointer<Void>const void *UnsafeRawPointerのSwiftマッピングに変換できます。 _ 3)および戻る。 Objective-Cでは、次のように記述します

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

(これらのキャストの正確な意味については、Clang ARCドキュメントの .2.4 Bridged casts を参照してください。)

Swiftには、そのためのUnmanagedタイプがあります。 UnsafePointer<Void>ではなくCOpaquePointerで動作するため、使用するのは少し面倒です。次に、2つのヘルパーメソッド(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) // ***
}

「複雑な」式は、Swiftの厳密な型システムを満たすためにのみ必要です。コンパイルされたコードでは、これは単なるポインター間のキャストです。 (「安全でない」メソッドを使用する場合は***コメントに示されているように短く書くことができますが、コンパイルされたコードは同じです。)

このヘルパーメソッドを使用すると、selfをC関数に次のように渡すことができます。

 let voidPtr = bridge(self)

(または、C関数が可変ポインターを必要とする場合はUnsafeMutablePointer<Void>(bridge(self)))、それをオブジェクトポインターに変換します。コールバック関数で– as

 let mySelf : MyType = bridge(voidPtr)

所有権の譲渡は行われないため、voidポインターが使用されている限り、selfが存在することを確認する必要があります。


完全を期すために、Objective-Cの__bridge_retainedおよび__bridge_transferと同等のSwiftは

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()は、オブジェクトポインターをvoidポインターにキャストし、オブジェクトを保持します。 bridgeTransfer()は、voidポインターをオブジェクトポインターに変換し、retainを消費します。

利点は、強い参照が保持されるため、呼び出し間でオブジェクトの割り当てを解除できないことです。欠点は、呼び出しのバランスを適切にとる必要があり、保持サイクルが発生しやすいことです。


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

「安全でないポインター」に関連する変更については、

91
Martin R

これはwithUnsafeMutablePointerの目的であるように思えます-任意のSwiftポインターをCポインターに変換します。しかし、私がテストしたコードは安全に動作します):

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

この答えは、 Martin Rの答え のように、コールバックのトピック固有ではありませんが、役に立つかもしれません...

通常、&演算子を使用して、安全でないvoidポインターに任意の型の値を渡すことができます。

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

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

ただし、selfを渡すには、selfが変更可能なコンテキストで渡す必要があります。つまり、参照型ではなく、値型の変更メソッド(またはinit)でこれを行う必要があります。

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

var myFooValue = FooValue()
myFooValue.bar()

参照型を使用する場合は、参照のローカルコピーを作成し、それにポインタを渡す必要があります。

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

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