web-dev-qa-db-ja.com

Swift)での無料のブリッジングとポインタアクセス

アプリをObjective-CからSwiftに移植していますが、次の方法を使用する必要があります。

CFStreamCreatePairWithSocketToHost(alloc: CFAllocator!, Host: CFString!, port: UInt32, \
readStream: CMutablePointer<Unmanaged<CFReadStream>?>, \
writeStream: CMutablePointer<Unmanaged<CFWriteStream>?>)

古いロジックは次のようになります(いくつかのWebサイトが同意しているようです)。

CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(Host), port, \
                                   &readStream, &writeStream);

NSInputStream inputStream = (__bridge_transfer NSInputStream *)readStream;
NSOutputStream outputStream = (__bridge_transfer NSOutputStream *)writeStream;

これは、フリーダイヤルのブリッジングのおかげでうまく機能します。ただし、「Swift-space」にはARCが存在せず、型システムが変更されました。

ストリームをのインスタンスに変換するにはどうすればよいですか

CMutablePointer<Unmanaged<CFReadStream>?>, and
CMutablePointer<Unmanaged<CFWriteStream>?>

そして、NSStream呼び出しの後で、それらをCFStreamCreatePairWithSocketToHostサブクラスに変換し直しますか?

23
Ephemera

私はそれを機能させました、これが私のコードです:接続クラスの参照をどこかに保持していることを確認してください:-)

class Connection : NSObject, NSStreamDelegate {
    let serverAddress: CFString = "127.0.0.1"
    let serverPort: UInt32 = 8443

    private var inputStream: NSInputStream!
    private var outputStream: NSOutputStream!

    func connect() {
        println("connecting...")

        var readStream:  Unmanaged<CFReadStream>?
        var writeStream: Unmanaged<CFWriteStream>?

        CFStreamCreatePairWithSocketToHost(nil, self.serverAddress, self.serverPort, &readStream, &writeStream)

        // Documentation suggests readStream and writeStream can be assumed to
        // be non-nil. If you believe otherwise, you can test if either is nil
        // and implement whatever error-handling you wish.

        self.inputStream = readStream!.takeRetainedValue()
        self.outputStream = writeStream!.takeRetainedValue()

        self.inputStream.delegate = self
        self.outputStream.delegate = self

        self.inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
        self.outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

        self.inputStream.open()
        self.outputStream.open()
    }

    func stream(stream: NSStream, handleEvent eventCode: NSStreamEvent) {
        println("stream event")
    }
}
42
Vincenzo

このスレッドで他の人が提供した例を機能させることができませんでした。確かに、それらはコンパイルされましたが、接続が開かれるとすぐにクラッシュしました。

ただし、WWDC 2014のディスカッション(およびiOS 8のリリースノート)で、NSStreamを初期化してイン/アウトストリームのバインドされたペアを作成するための新しい方法があることに気付きました。

下記参照:

var inputStream: NSInputStream?
var outputStream: NSOutputStream?

NSStream.getStreamsToHostWithName("localhost", port: 1234, inputStream: &inputStream, outputStream: &outputStream)

これにより、厄介なCFStreamCreatePairWithSocketToHost呼び出しが不要になるだけでなく、アンマネージリソースも不要になります。

28
Scott D

私はそれを行う方法を考え出しました。いくつかの重要な注意事項:

  1. &演算子を使用すると、CMutablePointersが自動的に作成されます。
  2. .getUnretainedValue()getRetainedValue()を使用してUnmanaged<T>Tに到達できます(.getUnretainedValue()__bridge_transferに類似しているようです)
  3. オプションは自動的にnilに初期化されます。
  4. オプションがnilの場合、それはfalse条件に変換されます。

これまでのところ、私は(テストされていません):

var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?

CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, Host, port, \
&readStream, &writeStream)

if (readStream && writeStream) {
    inputStream = readStream!.takeUnretainedValue();
    outputStream = writeStream!.takeUnretainedValue();
}
2
Ephemera

NSStreamクラスのgetStreamsToHostWithName関数を使用しています。 CFStreamCreatePairWithSocketToHostよりも簡単で優れています

func initNetworkCommunication(){

print("connecting...")

let serverAddress = "gzoa.vps.infomaniak.com"
let serverPort = 1234

NSStream.getStreamsToHostWithName(serverAddress, port: serverPort, inputStream: &inputStream, outputStream: &outputStream)

self.inputStream!.delegate = self
self.outputStream!.delegate = self

self.inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

self.inputStream!.open()
self.outputStream!.open()

}

1
Mr. Bean

CFのSwift3バージョンとNSコード。どちらも私のために機能します。

CF:

class Connection: NSObject, StreamDelegate {

private var inputStream: InputStream!
private var outputStream: OutputStream!

var connected = false

func connect(Host: String, port: UInt32) {
    var readStream:  Unmanaged<CFReadStream>?
    var writeStream: Unmanaged<CFWriteStream>?

    CFStreamCreatePairWithSocketToHost(nil, Host as CFString, port, &readStream, &writeStream)

    self.inputStream = readStream!.takeRetainedValue()
    self.outputStream = writeStream!.takeRetainedValue()

    if let inputStream = inputStream, let outputStream = outputStream {
        inputStream.delegate = self
        outputStream.delegate = self

        inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
        outputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)

        inputStream.open()
        outputStream.open()

        connected = true
    }
}

func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
    print("stream event, \(eventCode)")
}

}

NS:

class NSConnection: NSObject, StreamDelegate {

private var inputStream: InputStream?
private var outputStream: OutputStream?

var connected = false

func connect(Host: String, port: Int) {
    Stream.getStreamsToHost(withName: Host, port: port, inputStream: &inputStream, outputStream: &outputStream)

    if let inputStream = inputStream, let outputStream = outputStream {
        inputStream.delegate = self
        outputStream.delegate = self

        inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
        outputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)

        inputStream.open()
        outputStream.open()
    }
}

func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
    print("stream event, \(eventCode)")
}

}
1
Ivan Rep