简体   繁体   English

缓冲用作NSInputStream的NSOutputStream?

[英]Buffering NSOutputStream used as NSInputStream?

I have this consumer class that takes an NSInputStream as argument which will be processed async, and I want to push data that comes from a producer class that requires that it has an NSOutputStream provided as its output source. 我有这个使用NSInputStream作为参数的消费者类,它将被处理为异步,我想推送来自生产者类的数据,该生成器类要求它提供NSOutputStream作为其输出源。 Now how could I set up a buffering (or transparent) stream that acts as the output stream for the producer, and at the same time as the NSInputStream for my consumer class? 现在,我如何设置一个缓冲(或透明)流作为生产者的输出流,同时作为我的消费者类的NSInputStream?

I've looked a bit at the NSOutputStream +outputStreamToMemory and +outputStreamToBuffer:capacity: but haven't really figured out how to use it as input for an NSInputSource. 我看了一下NSOutputStream + outputStreamToMemory和+ outputStreamToBuffer:capacity:但还没有真正弄清楚如何将它用作NSInputSource的输入。

I had some idea of setting up a middle-man class that holds the actual buffer, then creating two subclasses (one for each NSInput/OutputStream) which holds a reference to this buffering class, and having these subclasses delegate most calls to that class, eg output subclass methods hasSpaceAvailable, write:maxLength:, and for the input, hasBytesAvailable, read:maxLength: etc. 我有一些想法,设置一个保存实际缓冲区的中间类,然后创建两个子类(每个NSInput / OutputStream一个),它保存对这个缓冲类的引用,并让这些子类委托大多数调用该类,例如,输出子类方法hasSpaceAvailable,write:maxLength:,并且对于输入,hasBytesAvailable,read:maxLength:etc.

Any tips on how to approach this situation are appreciated. 任何有关如何处理这种情况的提示都表示赞赏。 Thanks. 谢谢。

One way to accomplish this would be to use the example code on the apple developer site. 实现此目的的一种方法是使用apple开发人员站点上的示例代码。 SimpleURLConnection example SimpleURLConnection示例

This is how to do it, as can be seen in the PostController.m code 这是如何做到的,如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

Basically you attach the ends of two streams together with a buffer. 基本上,您使用缓冲区将两个流的末端连接在一起。

You might want to consider subclassing NSInputStream, and wrapping the source stream in your new class that buffers and/or modifies the bytes as they pass through. 您可能需要考虑子类化NSInputStream,并将源流包装在新类中,以便在通过时缓冲和/或修改字节。

The main reason I found for doing this over the bound sockets approach is to support seeking. 我通过绑定套接字方法找到的主要原因是支持搜索。 File based NSInputStreams use a stream property to seek within the file, and I couldn't easily arrange this without subclassing. 基于文件的NSInputStreams使用stream属性在文件中进行搜索,如果没有子类化,我就无法轻松地进行排序。

A problem with this approach is that it seems toll-free bridging won't work for you subclass - but there is a very nice article that will also give you a template subclass to start from if you need one: 这种方法的一个问题是,似乎免费的桥接对你的子类不起作用 - 但是有一篇非常好的文章,如果你需要的话,它还会给你一个模板子类:

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

I got a buffering solution working using both approaches - although another issue I had with the subclass approach is that you need to take care to send events to listeners appropriately - for example, when your source stream sends you an EOF event, you won't pass it on to your consumer until they have emptied the buffer - so there is some messing about to do there. 我使用这两种方法都有一个缓冲解决方案 - 虽然我对子类方法的另一个问题是你需要注意将事件发送给听众 - 例如,当你的源流发送给你一个EOF事件时,你不会将它传递给你的消费者,直到他们清空了缓冲区 - 所以那里有一些乱七八糟的东西。

Also - you might need to ensure that clients do their reading off the main run loop (I got it working with grand central dispatch) - because any observing you do in your subclass - on the source stream - will clash with the consumer otherwise. 另外 - 您可能需要确保客户端从主运行循环中读取它(我使用了大型中央调度) - 因为您在子类中执行的任何观察 - 在源流上 - 否则将与消费者冲突。 Although you appear to be able to pick any run loop to observe streams on, only the main one works. 虽然您似乎能够选择任何运行循环来观察流,但只有主循环可以工作。

So overall I'd say go with the paired streams unless you need to support seeking - or are particularly averse to the paired streams method. 总的来说,除非你需要支持搜索 - 或者特别厌恶配对的流方法,否则我会说配对流。

Here is an already implemented class that does exactly what you want 这是一个已经实现的类,它完全符合您的要求

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

Anyone still using Objecive C, as of iOS 8 this is the canonical way to do it: 任何仍然使用Objecive C的人,从iOS 8开始,这是规范的方式:

NSStream:getBoundStreamWithBufferSize:inputStream:outputStream: NSStream:getBoundStreamWithBufferSize:的inputStream:OutputStream中:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM