2010-07-10 12 views
13

Ho questa classe consumer che accetta un NSInputStream come argomento che verrà elaborato async, e voglio spingere i dati che provengono da una classe producer che richiede che abbia un NSOutputStream fornito come sorgente di output. Ora, come posso configurare un flusso di buffering (o trasparente) che funge da flusso di output per il produttore e contemporaneamente da NSInputStream per la mia classe consumer?Buffering NSOutputStream utilizzato come NSInputStream?

Ho guardato un po 'NSOutputStream + outputStreamToMemory e + outputStreamToBuffer: capacità: ma non ho davvero capito come utilizzarlo come input per un NSInputSource.

Avevo un'idea di come impostare una classe uomo medio che contiene il buffer effettivo, quindi creare due sottoclassi (una per ogni NSInput/OutputStream) che contiene un riferimento a questa classe di buffer e che queste sottoclassi delegano la maggior parte delle chiamate a tale classe, ad esempio metodi di sottoclasse di output hasSpaceAvailable, write: maxLength :, e per l'input, hasBytesAvailable, read: maxLength: ecc.

Sono graditi suggerimenti su come affrontare questa situazione. Grazie.

+0

Questa è una grande domanda, sto lottando per trovare una risposta ad esso troppo! Quello che ho scoperto finora è usare un file come un intermediario (che non sembra essere molto efficiente). Spero che qualcuno trovi una soluzione migliore – Nick

+0

Ho ricevuto alcuni suggerimenti nei forum di apple dev che mi hanno indirizzato a CFStreamCreateBoundPair. C'è un codice di esempio per questo nel progetto di esempio SimpleURLConnections. Puoi anche consultare https://devforums.apple.com/it/message/258868#258868. Tuttavia non l'ho più esaminato dopo. – cahlbin

risposta

10

Un modo per ottenere ciò sarebbe utilizzare il codice di esempio sul sito degli sviluppatori di Apple. SimpleURLConnection example

Questo è come farlo, come si può vedere nel codice PostController.m

@interface NSStream (BoundPairAdditions) 
+ (void)createBoundInputStream:(NSInputStream **)inputStreamPtr outputStream:(NSOutputStream **)outputStreamPtr bufferSize:(NSUInteger)bufferSize; 
@end 

@implementation NSStream (BoundPairAdditions) 

+ (void)createBoundInputStream:(NSInputStream **)inputStreamPtr outputStream:(NSOutputStream **)outputStreamPtr bufferSize:(NSUInteger)bufferSize 
{ 
    CFReadStreamRef  readStream; 
    CFWriteStreamRef writeStream; 

    assert((inputStreamPtr != NULL) || (outputStreamPtr != NULL)); 

    readStream = NULL; 
    writeStream = NULL; 

    CFStreamCreateBoundPair(
     NULL, 
     ((inputStreamPtr != nil) ? &readStream : NULL), 
     ((outputStreamPtr != nil) ? &writeStream : NULL), 
     (CFIndex) bufferSize); 

    if (inputStreamPtr != NULL) { 
     *inputStreamPtr = [NSMakeCollectable(readStream) autorelease]; 
    } 
    if (outputStreamPtr != NULL) { 
     *outputStreamPtr = [NSMakeCollectable(writeStream) autorelease]; 
    } 
} 
@end 

In sostanza si collega l'estremità di due corsi d'acqua insieme ad un tampone.

+0

Il metodo handleEvent per dataReceived si verifica quando il metodo viene richiamato (nello stesso stack di metodi) o verrà attivato in un altro evento? –

+0

Vuoi dire se il metodo handleEvent sarà nello stesso stack di chiamate di createBoundInputStream? IIRC, no. – JugsteR

+0

No, intendevo dire se il metodo handleEvent per inputStream sarà sullo stesso stack di chiamate del metodo di scrittura chiamato su outputStream? O sarà programmato come nuovo evento? –

1

È possibile considerare la sottoclasse di NSInputStream e il wrapping del flusso di origine nella nuova classe che memorizza e/o modifica i byte durante il loro passaggio.

Il motivo principale per cui ho trovato questo approccio rispetto ai socket vincolati è il supporto alla ricerca. Gli NSInputStreams basati su file utilizzano una proprietà del flusso da cercare all'interno del file e non potrei facilmente organizzarla senza creare sottoclassi.

Un problema di questo approccio è che sembra il numero verde di bridging non funziona per voi sottoclasse - ma c'è un articolo molto bello che vi darà anche una sottoclasse modello di partire da se avete bisogno di uno:

http://bjhomer.blogspot.co.uk/2011/04/subclassing-nsinputstream.html

ho ottenuto una soluzione di buffer di lavoro utilizzando entrambi gli approcci - anche se un altro problema che ho avuto con l'approccio sottoclasse è che avete bisogno di prendersi cura di inviare gli eventi di ascoltatori in modo appropriato - per esempio, quando il flusso di origine invia una Evento EOF, non lo inoltrerai al tuo consumatore fino a quando non svuoteranno il buffer, quindi c'è un po 'di confusione da fare lì.

Inoltre, potrebbe essere necessario assicurarsi che i client eseguano la lettura dal ciclo di esecuzione principale (ho funzionato con invio centralizzato), perché qualsiasi osservazione effettuata nella sottoclasse, nel flusso di origine, si scontrerà con il altrimenti. Sebbene sembri in grado di selezionare un ciclo di esecuzione per osservare i flussi, solo quello principale funziona.

Quindi, in generale, direi di andare con i flussi accoppiati a meno che non sia necessario supportare la ricerca - o sono particolarmente avversi al metodo degli stream associati.

0

Ecco una classe già implementato che fa esattamente quello che vuoi

BufferOutputStreamToInputStream

// initialize 
self.bufferWriter = [[BufferOutputStreamToInputStream alloc] init]; 
[self.bufferWriter openOutputStream]; 

// later you want to set the delegate of the inputStream and shedule it in runloop 
// remember, you are responsible for the inputStream, the outputStream is taken care off;) 
self.bufferWriter.inputStream.delegate = self; 
[self.bufferWriter.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
[self.bufferWriter.inputStream open] 

// fill with data when desired on some event  
[self.bufferWriter addDataToBuffer:someData];