Ponte gratuita e acesso por ponteiro em Swift
-
21-12-2019 - |
Pergunta
Estou portando um aplicativo de Objective-C para Swift e preciso usar o seguinte método:
CFStreamCreatePairWithSocketToHost(alloc: CFAllocator!, host: CFString!, port: UInt32, \
readStream: CMutablePointer<Unmanaged<CFReadStream>?>, \
writeStream: CMutablePointer<Unmanaged<CFWriteStream>?>)
A lógica antiga é assim (com a qual vários sites parecem concordar):
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;
O que funciona bem graças à ponte gratuita.No entanto, o ARC não existe no "Swift-space" e o sistema de tipos mudou.
Como faço para transformar meus streams em instâncias de
CMutablePointer<Unmanaged<CFReadStream>?>, and
CMutablePointer<Unmanaged<CFWriteStream>?>
E então converta-os novamente em NSStream
subclasses após o CFStreamCreatePairWithSocketToHost
chamar?
Solução
Eu fiz funcionar, aqui está meu código:Certifique-se de manter uma referência da classe de conexão em algum lugar :-)
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")
}
}
Outras dicas
Não consegui fazer com que os exemplos que outros forneceram neste tópico funcionassem.Claro, eles compilaram, mas travaram assim que a conexão foi aberta.
No entanto, notei nas discussões da WWDC 2014 (e nas notas de lançamento do iOS 8) que existe um novo método para inicializar um NSStream para criar um par vinculado de fluxos de entrada/saída.
Veja abaixo:
var inputStream: NSInputStream?
var outputStream: NSOutputStream?
NSStream.getStreamsToHostWithName("localhost", port: 1234, inputStream: &inputStream, outputStream: &outputStream)
Isso elimina a necessidade da chamada CFStreamCreatePairWithSocketToHost estranha, bem como elimina a necessidade de recursos não gerenciados.
Eu descobri como fazer isso.Algumas notas importantes:
CMutablePointers
será criado automaticamente se você usar o operador &.- Você pode chegar no
T
em umUnmanaged<T>
com.getUnretainedValue()
egetRetainedValue()
(Parece.getUnretainedValue()
é análogo a__bridge_transfer
) - Os opcionais são inicializados automaticamente para
nil
. - Se um opcional for
nil
isso se traduzirá em umfalse
doença.
Até agora eu tenho (não testado):
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, \
&readStream, &writeStream)
if (readStream && writeStream) {
inputStream = readStream!.takeUnretainedValue();
outputStream = writeStream!.takeUnretainedValue();
}
Estou usando a função getStreamsToHostWithName da classe NSStream.É mais fácil e melhor que 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()
}
Versão Swift 3 do código CF e NS.Ambos funcionam para mim.
FC:
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)")
}
}
P:
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)")
}
}