Буферизация NSOutputStream используется как NSInputStream? - PullRequest
14 голосов
/ 11 июля 2010

У меня есть этот потребительский класс, который принимает NSInputStream в качестве аргумента, который будет обрабатываться асинхронно, и я хочу отправить данные, которые поступают из класса производителя, для которого требуется, чтобы в качестве источника вывода был указан NSOutputStream.Теперь, как я могу установить буферный (или прозрачный) поток, который действует как выходной поток для производителя, и в то же время как NSInputStream для моего потребительского класса?

Я немного посмотрел наNSOutputStream + outputStreamToMemory и + outputStreamToBuffer :acity: но на самом деле не выяснили, как использовать его в качестве входных данных для NSInputSource.

У меня была идея создать класс среднего класса, который содержит реальный буфер,затем создать два подкласса (по одному для каждого NSInput / OutputStream), который содержит ссылку на этот класс буферизации, и заставить эти подклассы делегировать большинство вызовов этому классу, например, методы выходного подкласса hasSpaceAvailable, write: maxLength :, и для ввода hasBytesAvailable,читай: maxLength: и т. д.

Любые советы о том, как подойти к этой ситуации, приветствуются.Благодарю.

Ответы [ 4 ]

11 голосов
/ 06 декабря 2011

Один из способов сделать это - использовать пример кода на сайте разработчика Apple. SimpleURLConnection пример

Вот как это сделать, как видно из кода 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

Обычно вы присоединяете концы двух потоков вместе с буфером.

1 голос
/ 19 марта 2012

Возможно, вы захотите рассмотреть вопрос о создании подкласса NSInputStream и оборачивании исходного потока в ваш новый класс, который буферизует и / или изменяет байты по мере их прохождения.

Основная причина, которую я нашел для этого через границыПодход сокетов заключается в поддержке поиска.Файловые NSInputStreams используют свойство потока для поиска в файле, и я не мог легко организовать это без подклассов.

Проблема с этим подходом состоит в том, что кажется, что бесплатное мостовое соединение не будет работать для вашего подкласса- но есть очень хорошая статья, которая также даст вам подкласс шаблона, с которого нужно начинать:

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

Я получил решение для буферизации, работающее с использованием обоих подходов - хотяЕще одна проблема, связанная с подходом подкласса, заключается в том, что вам нужно позаботиться о том, чтобы отправлять события слушателям соответствующим образом - например, когда ваш исходный поток отправляет вам событие EOF, вы не будете передавать его своему потребителю, пока он не освободитбуфер - так что там есть некоторые проблемы.

Кроме того, вам может потребоваться убедиться, что клиенты выполняют чтение из основного цикла выполнения (я понял, что это работает с большой центральной диспетчеризацией), - потому что любой наблюдает за вамиdo в вашем подклассе - в исходном потоке - конфликтует с потребителем в противном случае.Хотя вы, похоже, можете выбрать любой цикл выполнения для наблюдения за потоками, работает только основной.

Таким образом, в целом я бы сказал, что нужно использовать парные потоки, если вам не нужна поддержка поиска - или если вы не совсем увереныв метод парных потоков.

0 голосов
/ 30 мая 2018

Любой, кто все еще использует Objecive C, для iOS 8 это канонический способ сделать это:

NSStream: getBoundStreamWithBufferSize: inputStream: outputStream:

0 голосов
/ 04 февраля 2015

Вот уже реализованный класс, который делает именно то, что вы хотите

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];
...