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
TObject
class, the base class for the majority of MacApp classes, contains two methods for streaming data:ReadFrom
andWriteTo
. All classes that descend fromTObject
can override these methods to read or write their data, using theTStream
parameter passed toReadFrom
andWriteTo
.- 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.
TStream
class 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 toReadBytes
orWriteBytes
.In the
TStream
class, theReadBytes
andWriteBytes
methods are unimplemented and are left as a responsibility of the subclass. The following subclasses ofTStream
implementReadBytes
andWriteBytes
to read and write data for specific destinations:
TCountingStream
- The
TCountingStream
class counts the bytes in a stream without writing them to a real destination; it is not used for reading.TDragFlavorStream
- The
TDragFlavorStream
class calls Drag Manager routines to read and write flavor data.TFileResourceStream
- This subclass of
TResourceStream
reads from or writes to a resource handle.TFileResourceStream
has an associated file and uses it to set the current resource file before reading or writing.TFileStream
- The
TFileStream
class reads from or writes to a file.THandleStream
- The
THandleStream
class reads from or writes to a memory handle.TResourceStream
- The
TResourceStream
class reads from or writes to a resource handle.TSectionStream
- The
TSectionStream
class 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
WriteTo
method 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 theReadFrom
method to read the data for a class from a stream, you read only the data you wrote in theWriteTo
method, in exactly the same order.
TStream
includes theReadStreamObject
andWriteStreamObject
methods for reading and writing an entire object. Classes that support the use ofReadStreamObject
andWriteStreamObject
(including all of MacApp's view classes) override theGetStandardSignature
method to supply a unique signature ID. For example, MacApp defines the following signature ID to be returned by theGetStandardSignature
method of theTButton
class:
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.
TStream
uses aTContext
object 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
GetDataAsHandle
andGetDataStream
methods.TFileBasedDocument
- This class uses a counting stream in its
DoNeedDiskSpace
method 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 itsDoReadScript
andDoWriteScript
methods, to read a script from a file or write one to a file.TSection
- This class uses a section stream in its
DoRead
andDoWrite
methods, to read or write section data.TViewServer
- This class creates a handle stream in its
DoMakeViewStream
method. The view server'sReadViewsFromResource
andWriteViewsToHandle
methods call onDoMakeViewStream
to 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 createReadFrom
andWriteTo
methods, 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 modifyTStream
itself 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 * fStream
that references a stream object of any type. You can set thefStream
field by passing a stream to theISuperStream
method.
TSuperStream
can override theReadBytes
andWriteBytes
methods ofTStream
and make them defer to thefStream
field. For example,ReadBytes
would callfStream->ReadBytes
. (You may need to override other methods as well, such asGetPosition
andSetPosition
.) Now you can add a feature toTSuperStream
and use it with any stream type.