NSStream与NSInputStream小记

什么是NSStream?

在Cocoa中包含三个与流相关的类:NSStreamNSInputStreamNSOutputStream
NSStream是一个抽象基类,定义了所有流对象基础接口性。
NSInputStreamNSOutputStream继承自NSStream,实现了输入流输出流的默认行为。
下图描述了流的应用场景:
在这里插入图片描述

前置知识小纪

  • 从上图中看,NSInputStream可以从文件socketNSData对象中获取数据;
    NSOutputStream可以将数据写入文件、socket、内存缓存和NSData对象中。这三处类主要处理一些比较底层的任务。

  • 可以给流对象指定一个代理对象,如果没有指定,则流对象作为自己的代理。流对象调用唯一的代理方法stream:handleEvent:来处理流相关的事件。

  • 对于输入流来说,是有可用的数据可读取事件;我们可以使用read:maxLength:方法从流中获取数据。

  • 对于输出流来说,是准备好写入的数据事件;我们可以使用write:maxLength:方法将数据写入流。

Cocoa中的流对象与Core Foundation中的流对象是对应的。我们可以通过toll-free桥接方法来进行相互转换。NSStream、NSInputStream和NSOutputStream分别对应CFStream、CFReadStream和CFWriteStream。但这两者间不是完全一样的。Core Foundation一般使用回调函数来处理数据。另外我们可以子类化NSStream、NSInputStream和NSOutputStream,来自定义一些属性和行为,而Core Foundation中的流对象则无法进行扩展。

如何从流中读取数据

在这里插入图片描述

  • 从输入流中读取数据的步骤

(1)从数据源中创建和初始化一个NSInputStream实例
(2)将流对象放入一个run loop中并打开流
(3)处理流对象发送到其代理的事件
(4)当没有更多数据可读取时,关闭并销毁流对象。

  • 准备流对象

(1)要使用一个NSInputStream必须要有数据源
(2)数据源可以是文件、NSData对象和网络socket
(3)创建好后,我们设置其代理对象,并将其放入到run loop中然后打开流

代码清单1展示了这个准备过程.

- (void)setUpStreamForFile:(NSString *)path {
    // iStream is NSInputStream instance variable
    iStream = [[NSInputStream alloc] initWithFileAtPath:path];
    [iStream setDelegate:self];
    [iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
        forMode:NSDefaultRunLoopMode];
    [iStream open];
}

流对象放入run loop且有流事件(有可读数据)发生时流对象会向代理对象发送stream:handleEvent:消息。
在打开流之前,我们需要调用流对象的scheduleInRunLoop:forMode:方法,这样做可以避免在没有数据可读时阻塞代理对象的操作。
我们需要确保的是流对象被放入正确的run loop中,即放入流事件发生的那个线程的run loop中。

  • 查看流的属性

打开流后,我们可以使用streamStatus属性查看流的状态,用hasBytesAvailable属性检测是否有可读的数据,用streamError来查看流处理过程中产生的错误。

流一旦打开后,将会持续发送stream:handleEvent:消息给代理对象,直到流结束为止
这个消息接收一个NSStreamEvent常量作为参数,以标识事件的类型。
对于NSInputStream对象,主要的事件类型包括NSStreamEventOpenCompletedNSStreamEventHasBytesAvailableNSStreamEventEndEncountered
通常我们会对NSStreamEventHasBytesAvailable更感兴趣。代理清单2演示了从流中获取数据的过程:

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode) {
        case NSStreamEventHasBytesAvailable:
        {
            if (!data) {
                data = [NSMutableData data];
              }
            uint8_t buf[1024];
            unsigned int len = 0;
            len = [(NSInputStream *)aStream read:buf maxLength:1024];  // 读取数据
            if (len) {
                [data appendBytes:(const void *)buf length:len];
            }
        }
            break;
    }
}

当NSInputStream在处理流的过程中出现错误时,它将停止流处理并产生一个NSStreamEventErrorOccurred事件给代理。我们同样的上面的代理方法中来处理这个事件。

  • 清理流对象

当NSInputStream读取到流的结尾时,会发送一个NSStreamEventEndEncountered事件给代理,代理对象应该销毁流对象,此过程应该与创建时相对应。

代码清单3演示了关闭和释放流对象的过程:

代理清单3

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode) {
        case NSStreamEventEndEncountered:
        {
            [aStream close];
            [aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            aStream = nil;
        }
            break;
    }
}

NSStream和NSInputStream使用注意事项

  • NSStream的支持的方法如下:
- (void)open;
- (void)close;

@property (nullable, assign) id <NSStreamDelegate> delegate;
    // By default, a stream is its own delegate, and subclassers of NSInputStream and NSOutputStream must maintain this contract. [someStream setDelegate:nil] must restore this behavior. As usual, delegates are not retained.

- (nullable id)propertyForKey:(NSStreamPropertyKey)key;
- (BOOL)setProperty:(nullable id)property forKey:(NSStreamPropertyKey)key;

- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSRunLoopMode)mode;
- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSRunLoopMode)mode;

在这里插入图片描述 我们当继承NSInputStream或者NSOutputStream时,也必须实现NSStream中的方法。

  • 方法如下:
// NSInputStream is an abstract class representing the base functionality of a read stream.
// Subclassers are required to implement these methods.
@interface NSInputStream : NSStream
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len;
    // reads up to length bytes into the supplied buffer, which must be at least of size len. Returns the actual number of bytes read.

- (BOOL)getBuffer:(uint8_t * _Nullable * _Nonnull)buffer length:(NSUInteger *)len;
    // returns in O(1) a pointer to the buffer in 'buffer' and by reference in 'len' how many bytes are available. This buffer is only valid until the next stream operation. Subclassers may return NO for this if it is not appropriate for the stream type. This may return NO if the buffer is not available.

@property (readonly) BOOL hasBytesAvailable;
    // returns YES if the stream has bytes available or if it impossible to tell without actually doing the read.

- (instancetype)initWithData:(NSData *)data NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithURL:(NSURL *)url API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;

@end

在这里插入图片描述当继承NSInputStream时,也必须实现NSInputStream里面的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值