Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Programmer's Guide to MacApp / Part 2 - Working With MacApp
Chapter 27 - Working With Streams


Overview

A stream is a sequence of bytes that can be used to transfer data between locations. Most programmers are familiar with the use of streams for file I/O. MacApp provides stream classes that are useful for transferring data between a variety of destinations, such as files, memory, resources, and sections.

Writing a Document to a File Stream

MacApp's mechanism for saving a file-based document is described in Chapter 7, "Document Handling." In summary, a call is made to a document's SaveDocument method. For a TFileBasedDocument object, SaveDocument calls the SaveFile method of the document's file handler. The SaveFile method calls another file-handler method to save the file. Eventually, the file handler calls the document's DoWrite method, passing a reference to the document's file. Your document class overrides DoWrite to write its data.

TYourDocument::DoWrite(TFile* aFile, Boolean makingCopy)
{
   // Give parent class a chance to write its data.
   Inherited::DoWrite(aFile, makingCopy);

   // Create and init a file stream. Don't store it in a register.
   MAVolatileInit(TFileStream*, aFileStream, new TFileStream);
   aFileStream->IFileStream(aFile);

   // Write various data. Wrap the writing code in a failure handler
   // so that if a failure occurs, you can free the file stream.
   FailInfo fi;
   Try(fi)
   {  
      // Call the stream's WriteBytes method directly to write a color.
      theFileStream->WriteBytes( (void *) &fColor, sizeof(CRGBColor));

      // Write a Boolean field using a method of the stream.
      theFileStream->WriteBoolean(fBooleanFlag);

      // Call a stream method that writes an entire object.
      theFileStream->WriteStreamObject(fDataObject, kStandardObject);

      // Directly call the WriteTo method of an object to let the
      // object write its data to the stream.
      fOtherDataObject->WriteTo(theFileStream);
   }
   else  // If error occurs, free the file stream.
   {
      aFileStream = (TFileStream *)FreeIfObject(aFileStream);
      fi.ReSignal();
   }
   // Done with file stream, so free it now.
   aFileStream = (TFileStream *)FreeIfObject(aFileStream);

} // TYourDocument::DoWrite
Your DoWrite method first calls Inherited to allow its parent class to write any required data. It then creates and initializes a TFileStream object, using the MAVolatileInit macro to make sure the file stream object is not stored in a register.

The DoWrite method then writes out any persistent data (defined on page 51) for your document class. You can use any of the writing methods of the TStream class, knowing that the stream's destination is the document's file. The version of DoWrite shown above demonstrates some of the calls your document might require to write its data.

Reading a Document From a Stream

Reading a document file from a stream is similar to writing the document, but the action takes place in the DoRead method of your document object, shown on the following page.

void TYourDocument::DoRead(TFile* aFile, Boolean forPrinting)
{
   // Give parent class a chance to read its data.
   Inherited::DoRead(aFile, forPrinting);
   // Create and initialize a file stream. Don't store it in a register.
   MAVolatileInit(TFileStream*, aFileStream, new TFileStream);
   aFileStream->IFileStream(aFile);
   // Initialize a Boolean to specify whether we have created an object.
   MAVolatileInit(Boolean, haveObject, FALSE);
   TObject* theObject = NULL;// Passed by reference--doesn't need
                        // the MAVolatileInit macro.
   FailInfo fi;
   Try(fi)// Use failure handling in case of error.
   {
      // Call stream's ReadBytes method directly to read color.
      theFileStream->ReadBytes( (void *) &fColor, sizeof(CRGBColor));

      // Read a Boolean field using a method of the stream.
      fBooleanFlag = theFileStream->ReadBoolean();

      // Call a method to read an entire object and set field.
      haveObject = theFileStream->ReadStreamObject((TObject*&)
                                             theObject);
      if (haveObject) fDataObject = (TDataObject *) theObject;

      // Directly call the ReadFrom method of an object to let the
      // object read its data from the stream. (Assumes object
      // has already been created as part of creating the document.)
      fOtherDataObject->ReadFrom(theFileStream);
   }
   else
   {
      // Free object and stream if an error occurs.
      aFileStream = (TFileStream *)FreeIfObject(aFileStream);
      if (haveObject) fDataObject = (TDataObject *)
                              FreeIfObject(theObject);
      fi.ReSignal();// Pass on the failure.
   }
   // Done with file stream, so free it now.
   aFileStream = (TFileStream *)FreeIfObject(aFileStream);
} // TYourDocument::DoRead
Your document's DoRead method first calls Inherited to allow its parent class to read any required data. Then it creates and initializes a TFileStream object, using the MAVolatileInit macro to make sure the file stream object is not stored in a register. It also uses the macro to ensure that a Boolean variable, used in failure handling, is not stored in a register.

The DoRead method then reads any persistent data for the document object in the same order in which the data was written. The version of DoRead shown here demonstrates some of the calls your document might require to read its data. It makes these calls in the same order that similar calls were made by the DoWrite method to write the data.

WARNING
A failure to read data in the exact order it was written will lead to trashed data at best and to program crashes at worst. This is probably the number one cause of bugs in using streams.

Previous Book Contents Book Index Next

© Apple Computer, Inc.
25 JUL 1996