Important: The information in this document is obsolete and should not be used for new development.
Streams
A stream is, literally, a stream of bytes that can flow from one location to another. Streams can be used for many purposes:
Streaming in MacApp depends on two features:
- to save object data and use the saved data to recreate the original objects
- to read data from a file, memory handle, or other destination
- to write data to a file, memory handle, or other destination
- to count the number of bytes in a data stream
The
- The
TObjectclass, the base class for the majority of MacApp classes, contains two methods for streaming data:ReadFromandWriteTo. All classes that descend fromTObjectcan override these methods to read or write their data, using theTStreamparameter passed toReadFromandWriteTo.- MacApp supplies stream classes for a variety of destinations, including files, resources, handles, sections, and drag flavors (flavor data is described in Chapter 9, "Drag and Drop.") Your objects read and write their data the same way every time--they don't have to worry about the final destination.
TStreamclass is an abstract class that contains methods for reading and writing many primitive data types, such as bytes, integers, characters, points, rectangles, and strings. When an application calls one of these methods, passing data of the appropriate type, the method transfers the data with a call toReadBytesorWriteBytes.In the
TStreamclass, theReadBytesandWriteBytesmethods are unimplemented and are left as a responsibility of the subclass. The following subclasses ofTStreamimplementReadBytesandWriteBytesto read and write data for specific destinations:
TCountingStream- The
TCountingStreamclass counts the bytes in a stream without writing them to a real destination; it is not used for reading.TDragFlavorStream- The
TDragFlavorStreamclass calls Drag Manager routines to read and write flavor data.TFileResourceStream- This subclass of
TResourceStreamreads from or writes to a resource handle.TFileResourceStreamhas an associated file and uses it to set the current resource file before reading or writing.TFileStream- The
TFileStreamclass reads from or writes to a file.THandleStream- The
THandleStreamclass reads from or writes to a memory handle.TResourceStream- The
TResourceStreamclass reads from or writes to a resource handle.TSectionStream- The
TSectionStreamclass reads and writes data for a section (sections are described in "Publish and Subscribe," beginning on page 184).Streaming Object Data
Persistent data is the data needed to recreate an object in the same state as when it wrote its data. For example, to recreate a view with its previous size and location, the persistent data must include the size and location. But if the view is always re-created with a default size and location, its persistent data need not include size and location.When you override the
WriteTomethod to stream the data for a class, you generally need to write only its persistent data, because any constant data can be initialized automatically. When you override theReadFrommethod to read the data for a class from a stream, you read only the data you wrote in theWriteTomethod, in exactly the same order.
TStreamincludes theReadStreamObjectandWriteStreamObjectmethods for reading and writing an entire object. Classes that support the use ofReadStreamObjectandWriteStreamObject(including all of MacApp's view classes) override theGetStandardSignaturemethod to supply a unique signature ID. For example, MacApp defines the following signature ID to be returned by theGetStandardSignaturemethod of theTButtonclass:
const IDType kStdButton = 'butn';Objects are streamed using the following mechanisms to ensure efficiency:
- Class names and signature IDs are used to identify objects. A class name is written only once per class, not once per object of that class.
- Objects are created by signature, if available; otherwise, by class name.
TStreamuses aTContextobject to maintain a table of all the objects written out. The first time an object is written it is added to the table. If another reference to the same object is encountered, only its table index is written. A similar process is used when reading objects back.- If an object is not recognized when reading from a stream, the data for that object is skipped over.
Examples of Using Streams in MacApp
You can find useful examples of how to use the different types of streams in the MacApp class library. For example:
For additional information on using streams in your application, see
TDragItem- This class uses a drag flavor stream in its
GetDataAsHandleandGetDataStreammethods.TFileBasedDocument- This class uses a counting stream in its
DoNeedDiskSpacemethod to determine how much space the file will take on disk. Your document subclasses can use a similar approach. It also uses a file stream in itsDoReadScriptandDoWriteScriptmethods, to read a script from a file or write one to a file.TSection- This class uses a section stream in its
DoReadandDoWritemethods, to read or write section data.TViewServer- This class creates a handle stream in its
DoMakeViewStreammethod. The view server'sReadViewsFromResourceandWriteViewsToHandlemethods call onDoMakeViewStreamto supply a stream.
Chapter 27, "Working With Streams."Advantages of Using Streams
One great benefit of using streams is that if your classes know how to read and write their data for one kind of stream, they can read and write for any kind of stream. If you createReadFromandWriteTomethods, your application's classes will be able to read and write their data to document files, memory handles, and other destinations. They will also be able to use a counting stream to count bytes and determine how much storage is needed.Another advantage of using streams is that you can associate a failure handler with a stream operation, which gives you explicit control when an error occurs during the reading and writing of documents or other destinations.
Disadvantages of Using Streams
Although MacApp's stream classes can serve many purposes, they do have inherent limitations. If any of these limitations represents a serious problem for your application, make sure your design includes an adequate solution before using streams.File Formats
Using the stream mechanism to write a document can result in a file format that varies widely from document to document and is difficult to explain. The data for a document is written out by the object hierarchy that exists at the time the document is written, and it can be read in conveniently only by recreating that same set of objects.MacApp writes a length word with each object, so it is possible to skip over objects your application doesn't understand. However, information in a skipped object is effectively lost to your application and may prevent the recreation of a meaningful document.
Because a file format based on an object hierarchy can vary from document to document, it is difficult to support backward compatibility across document versions. For example, if you create a new version of your application that uses new or modified objects, it may be difficult to ensure that the new objects can read data written by the old ones.
One solution is to write a unique tag with each object, and change the tag if the object format changes in a future version of the application. With some effort, a tagged format can be built on top of MacApp's implementation of streams.
Speed and Data Access
MacApp's stream classes provide basic persistent object support, but they are not an object database. If you need to create files with large numbers of objects, and especially if you want random read/write access to objects, you can use MacApp's streams to pack objects into handy chunks, but you will need to use some other software to access the chunks on disk. Third-party products are available for this purpose.Ease of Modification
The class hierarchy of MacApp's stream classes can make it difficult to modify stream behavior across all classes without changing MacApp code. For example, if you want to add a feature that works for all stream classes, you have to either modifyTStreamitself in the MacApp code or create a separate subclass forTFileStream,THandleStream, and each of the other stream classes.One way to work around this is to create your own subclass of
TStream, such asTSuperStream, and give it a fieldTStream * fStreamthat references a stream object of any type. You can set thefStreamfield by passing a stream to theISuperStreammethod.
TSuperStreamcan override theReadBytesandWriteBytesmethods ofTStreamand make them defer to thefStreamfield. For example,ReadByteswould callfStream->ReadBytes. (You may need to override other methods as well, such asGetPositionandSetPosition.) Now you can add a feature toTSuperStreamand use it with any stream type.