14

我有这个消费者类,它将一个 NSInputStream 作为参数,将被异步处理,我想推送来自生产者类的数据,该生产者类要求它提供一个 NSOutputStream 作为其输出源。现在我如何设置一个缓冲(或透明)流作为生产者的输出流,同时作为我的消费者类的 NSInputStream?

我看了一点 NSOutputStream +outputStreamToMemory 和 +outputStreamToBuffer:capacity: 但还没有真正弄清楚如何将它用作 NSInputSource 的输入。

我有一些想法,即建立一个包含实际缓冲区的中间人类,然后创建两个子类(每个 NSInput/OutputStream 一个),它们包含对该缓冲类的引用,并让这些子类将大多数调用委托给该类,例如,输出子类方法 hasSpaceAvailable、write:maxLength: 和输入的 hasBytesAvailable、read:maxLength: 等。

任何有关如何处理这种情况的提示都值得赞赏。谢谢。

4

4 回答 4

11

实现此目的的一种方法是使用苹果开发者网站上的示例代码。 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

基本上,您将两个流的末端与一个缓冲区连接在一起。

于 2011-12-06T14:43:43.753 回答
1

您可能需要考虑将 NSInputStream 子类化,并将源流包装在新类中,以便在字节通过时缓冲和/或修改字节。

我发现通过绑定套接字方法执行此操作的主要原因是支持搜索。基于文件的 NSInputStreams 使用流属性在文件中查找,如果没有子类化,我无法轻松地安排它。

这种方法的一个问题是,免费桥接似乎不适用于您的子类 - 但是有一篇非常好的文章,如果您需要,它也会为您提供一个模板子类:

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

我得到了一个使用这两种方法的缓冲解决方案——尽管我在子类方法中遇到的另一个问题是你需要注意适当地向侦听器发送事件——例如,当你的源流向你发送一个 EOF 事件时,你不会将其传递给您的消费者,直到他们清空缓冲区 - 所以那里有一些事情要做。

此外 - 您可能需要确保客户端读取主运行循环(我让它与大型中央调度一起使用) - 因为您在子类中所做的任何观察 - 在源流上 - 否则将与消费者发生冲突。尽管您似乎可以选择任何运行循环来观察流,但只有主循环有效。

所以总的来说,我会说使用配对流,除非您需要支持搜索 - 或者特别反对配对流方法。

于 2012-03-19T10:41:23.507 回答
0

这是一个已经实现的类,它完全符合您的要求

缓冲区输出流到输入流

// 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];
于 2015-02-04T10:01:46.970 回答
0

任何仍在使用 Objecive C 的人,从 iOS 8 开始,这是执行此操作的规范方法:

NSStream:getBoundStreamWithBufferSize:inputStream:outputStream:

于 2018-05-29T21:43:41.237 回答