Буферизация NSOutputStream используется как NSInputStream?
У меня есть этот потребительский класс, который принимает NSInputStream в качестве аргумента, который будет обрабатываться async, и я хочу выталкивать данные, которые поступают от класса-производителя, который требует, чтобы у него был NSOutputStream, предоставленный в качестве исходного источника. Теперь как я могу настроить буферный (или прозрачный) поток, который действует как выходной поток для производителя, и одновременно с NSInputStream для моего потребительского класса?
Я немного посмотрел на NSOutputStream + outputStreamToMemory и + outputStreamToBuffer: емкость: но на самом деле не понял, как использовать его как вход для NSInputSource.
У меня было некоторое представление о создании класса среднего класса, в котором хранится фактический буфер, а затем создается два подкласса (по одному для каждого NSInput/OutputStream), который содержит ссылку на этот класс буферизации и что эти подклассы делегируют большинство вызовов этот класс, например, методы подкласса вывода hasSpaceAvailable, write: maxLength:, и для ввода hasBytesAvailable, read: maxLength: и т.д.
Приветствуются любые советы о том, как подойти к этой ситуации. Благодарю.
Ответы
Ответ 1
Один из способов добиться этого - использовать код примера на веб-сайте разработчика 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
В основном вы присоединяете концы двух потоков вместе с буфером.
Ответ 2
Возможно, вы захотите рассмотреть подкласс NSInputStream и обернуть исходный поток в новом классе, который буферизирует и/или изменяет байты по мере их прохождения.
Основная причина, по которой я нашел это для подхода с привязанными сокетами, - это поддержка поиска. Файловые NSInputStreams используют свойство stream для поиска в файле, и я не мог легко организовать это без подкласса.
Проблема с этим подходом заключается в том, что кажется, что бесплатный мост не будет работать для вашего подкласса, но есть очень хорошая статья, которая также даст вам подкласс класса, чтобы начать, если вам это нужно:
http://bjhomer.blogspot.co.uk/2011/04/subclassing-nsinputstream.html
У меня есть буферное решение, работающее с использованием обоих подходов. Хотя еще одна проблема, с которой я столкнулась с подклассовым подходом, заключается в том, что вам нужно позаботиться о том, чтобы отправлять события слушателям соответствующим образом - например, когда ваш исходный поток отправляет вам событие EOF, вы не передадут его вашему потребителю до тех пор, пока они не опустошат буфер - так что там есть какая-то ошибка.
Кроме того, вам может потребоваться убедиться, что клиенты выполняют чтение с основного цикла запуска (я работал с большой центральной отправкой) - потому что любое наблюдение за вами в вашем подклассе - в потоке источника - будет конфликтовать с потребителем в противном случае. Хотя вы, кажется, можете выбрать любой цикл выполнения для наблюдения за потоками, работает только основной.
В общем, я бы сказал, что нужно идти с парными потоками, если вам не нужно поддерживать поиск - или особенно не склонны к методу парных потоков.
Ответ 3
Вот уже реализованный класс, который делает именно то, что вы хотите
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];