Streams provide an easy way for a program to exchange data with a variety of media in a device-independent way. A stream is a contiguous sequence of bits transmitted serially over a communications path. It is unidirectional and hence, from the perspective of a program, a stream can be an input (or read) stream or an output (or write) stream. Except for file-based streams, streams are non-seekable—once stream data has been provided or consumed, it cannot be retrieved again from the stream.
Cocoa includes three stream-related classes:
NSStream is an abstract class that defines the fundamental interface and properties for all stream objects.
NSOutputStream are subclasses of
NSStream and implement default input-stream and output-stream behavior. You can create
NSOutputStream instances for stream data located in memory or written to a file or C buffer; you can create
NSInputStream instances for stream data read from an
NSData object or a file. You can also have
NSOutputStream objects at the end points of a socket-based network connection and you can use stream objects without loading all of the stream data into memory at once. Figure 1 illustrates the types of input-stream and output-stream objects in terms of their sources or destinations.
Because they deal with such a basic computing abstraction (streams),
NSStream and its subclasses are intended for lower-level programming tasks. If there is a higher-level Cocoa API that is more suited for a particular task (for example,
NSFileHandle) use it instead.
Stream objects have properties associated with them. Most properties have to do with network security and configuration, namely secure-socket (SSL) levels and SOCKS proxy information. Two important additional properties are
NSStreamDataWrittenToMemoryStreamKey, which permits retrieval of data written to memory for an output stream, and
NSStreamFileCurrentOffsetKey, which allows you to manipulate the current read or write position in file-based streams.
A stream object also has a delegate associated with it. If a delegate is not explicitly set, the stream object itself becomes the delegate (a useful convention for custom subclasses). A stream object invokes the sole delegation method
stream:handleEvent: for each stream-related event it handles. Of particular importance are the events that indicate when bytes are available to read from an input stream and when an output stream signals that it’s ready to accept bytes. For these two events, the delegate sends the stream the appropriate message—
write:maxlength:, depending on type of stream—to get the bytes from the stream or to put bytes on the stream.
NSStream is built on the
CFStream layer of Core Foundation. This close relationship means that the concrete subclasses of
NSInputStream, are toll-free bridged with their Core Foundation counterparts
CFReadStream. Although there are strong similarities between the Cocoa and Core Foundation stream APIs, their implementations are not exactly coincident. The Cocoa stream classes use the delegation model for asynchronous behavior (assuming run-loop scheduling) while Core Foundation uses client callbacks. The Core Foundation stream types sets the client (termed a context in Core Foundation) differently than the NSStream sets the delegate; calls to set the delegate should not be mixed with calls to set the context. Otherwise you can freely intermix calls from the two APIs in your code.
Despite their strong similarities,
NSStream does give you a major advantage over
CFStream. Because of its Objective-C underpinnings, it is extensible. You can subclass
NSOutputStream to customize stream attributes and behavior. 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
NSOutputStream. See the reference documentation for
NSOutputStream for details on subclassing these classes.