流是位数据通过通信路径的连续传送序列。它是单向的,从一个应用程序的角度,流可以是输入流(读操作流)或者输出流(写操作流),除了基于文件的流之外,其余的都是non-seekable的。一旦流数据被提供或者被使用,数据就不能够从流中获取到。
Cocoa包括三种与流有关的类:NSStream,NSInputStream,NSOutputStream. NSStream是抽象类,它定义了流对象的基本接口和属性。NSInputStream和NSOutputStream是NSStream的子类,它们实现了输入流和输出流的基本操作。你可以为存储在内存中,向文件或者C buffer写的流数据创建NSOutputStream对象;可以为从NSData对象和文件中读取的流数据创建NSInputStream对象;也可以在网络套接字的两端创建NSInputStream和NSOutputStream对象,通过流对象,你可以不用一次性将所有的流数据加载到内存中。下图是就输入流和输出流对象的源和目的地为依据对输入流和输出流的分类:
NSStream及其子类进行的是比较底层的开发,对于某些特殊的需求如果有顶层的Cocoa API更加适合的话(比如NSURL,NSFileHandle),那么就用顶层的API进行编程。
流对象有许多属性,大多数属性都和网络安全及其配置有关,也就是SSL和SOCKS代理信息。另外有两个重要的属性,一个是NSStreamDataWrittenToMemoryStreamKey,对于一个输出流它可以用来获取到写入内存中的数据。另一个是NSStreamFileCurrentOffsetKey,对于一个基于文件的流,可以用它操作读或者写的位置。
每个流对象都有一个与其相关联的delegate,如果其delegate没有显示的设置,那么这个流对象自身成为其delegate(对于自定义子类的话这是一个很有用的约定)。流对象调用它唯一的delegate方法stream:handleEvent:来处理所有与stream-related事件。对于传入参数中的events事件,它指示了什么时候输入流中有数据可供读入,什么时候输出流中有空间可供数据写入。对于这两个事件中的NSStreamEventHasBytesAvailable事件,delegate向该stream发送read:maxLength:消息从流中读取数据,对于NSStreamHasSpaceAvailable事件,delegate向该stream发送write:maxlength:向流中写入数据。
NSStream是建立在Core Foundation的CFStream层之上的。这层紧密的关系意味着NSStream的具体子类-NSInputStream和NSOutputStream与Core Foundation中的CFReadStream和CFWriteStream是一一对应的。尽管Cocoa和Core Foundation的stream APIs有很大的相似性,但是它们的实现却不尽相同,Cocoa stream类使用delegate模式来实现异步操作(比如将其布置在run loop之上),而Core Foundation使用客户端的回调。Core Foundation的stream类型设置的是client(在Core Foundation中叫做context),NSStream中设置的delegate,这是两个不同的概念,不应该把设置delegate和设置context搞混淆。
相比CFStream而言,NSStream有更强的可扩展性,你可以生成NSStream,NSInputStream,NSOutputStream的子类来自定义其属性和方法。For example, you could create an input stream that maintains statistics on the bytes it reads; or you could make a NSStream
subclass whose instances can seek through their stream, putting back bytes that have been read. NSStream
has its own set of required overrides, as do NSInputStream
and NSOutputStream
.