[futurebasic] Re: When to dispose of Hndl& and Ptr&?

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : December 1997 : Group Archive : Group : All Groups

From: Rick Brown <rbrown@...>
Date: Thu, 04 Dec 1997 22:28:28 -0600
Bill Kuck wrote:
> I am confused about when I should (or should not) include statements to
> dispose of handles and/or pointers.  

This is a really good question.  The basic rule of thumb is: If you're
calling a routine that _creates_ the handle, then you should dispose of
it as soon as you're no longer interested in the block that it
references.  If the routine just gives you a _copy_ of the handle (i.e.,
the handle was actually created (somewhere) _before_ you called the
routine, and the routine is simply telling you what the handle's value
is) then you should probably _not_ dispose of it, because chances are
quite good that other OS routines are going to expect that handle to be
valid even after you're no longer interested in it.

For example: The OpenPicture routine creates (and returns) a brand new
handle to you (into whose block subsequent QuickDraw commands will be
recorded).  You should dispose of that handle when you're done playing
with the picture.  On the other hand, the TEGetText function returns a
handle to an edit field's text--but that handle was actually created at
the time the _edit_field_ was created, and it must stay intact for as
long as the edit field exists.  You should _not_ dispose of this
handle--the OS disposes of it for you when you close the edit field (or
close the window).

Still, this begs the question: how do you _know_ whether a given routine
is creating a new handle for you (i.e., allocating a new relocatable
block), or just returning a handle to a pre-existing block?  The best
advice I can give is to study the routine's description carefully in
Inside Mac--they're usually pretty good about making that clear.  Also,
handle-giving routines whose names start with "GET" tend to return "old"
handles to you, while those which start with "NEW" tend to create new

> Consider the FN below that creates a
> resource file and does two things that got me to this state:
>       DEF DISPOSEH(H&)
> First, I read in a technical note, that we should never need to use CALL

Theoretically, you should never need to call RELEASERESOURCE _if_ the
resource in question is designated as purgeable.  If that's the case,
then the Resource Manager (or the Memory Manager?) will purge the
resource from memory if it should become necessary to reclaim that
memory for other purposes.
> Secondly, do we really need to dispose of handle H& since
> it is in an FN and will be essentially done away with when we exit the FN?

Handles created within a FN are _not_ disposed of when you exit the FN
(there are several good reasons for this, which we can discuss at
greater length later if you like).  Furthermore, handles (other than
resource handles) usually aren't designated as purgeable, so the Memory
Manager won't delete them when memory gets low.  If they're in the
application heap (as they usually tend to be), then they _will_ be
deleted when the app quits.  But you could run out of memory before
then, if you don't take care to dispose of them _within_ your program. 
This in fact is a very common programming bug, called "memory leak," in
which you frequently call some routine, or run through a lot of loop
iterations, in which a block is allocated (a handle is created), but you
never bother to dispose of it.  Each time through, a brand new block is
allocated, until you eventually run out of memory.
> I have seen several routines that use statements like these and then other
> times they don't given similar conditions.  Anyone have all this worked out
> and could summarize it for me?  BTW, this routine works fine if I give it a
> filename that  doesn't exist in the folder, otherwise it crashes on
> duplicating filenames - just after the "do you want to replace existing
> file?".
> DIM 80 mystring$(2)
> DIM volRefNum%,resRef%,osErr%,x%
> DIM H&
> LOCAL FN createRes
>   mystring$(1) = "First string in this STR# resource."
>   mystring$(2) = "Second string in this STR# resource."
>   filename$ = FILES$(_fSave,"Create fileƤ",,volRefNum%)
>   LONG IF filename$ <> ""
>     volRefNum% = FOLDER("",volRefNum%)
>     CALL CREATERESFILE(filename$)

If the file already exists (_and_ it has a resource fork) then you don't
have to call CREATERESFILE, (although doing so should be harmless,
according to Inside Mac).

>     resRef% = FN OPENRESFILE(filename$)
>     LONG IF resRef%

OpenResFile _always_ returns a nonzero ("true") value, so the above LONG
IF doesn't tell you anything.  OpenResFile returns -1 if some error
occurred.  You should check for that value, and then (if resRef% = -1)
do "OSErr = FN RESERROR" to see what the error actually was.

>       H& = FN NEWHANDLE _clear(2)
>       LONG IF H&
>         osErr% = FN HLOCK(H&)
>         POKE WORD [H&],0
>         FOR X% = 1 TO 2
>           DEF APNDSTR(mystring$(X%),H&)
>         NEXT X%
>         CALL ADDRESOURCE(H&,_"STR#",128,"")
>         CALL UPDATERESFILE(resRef%)
>         osErr% = FN HUNLOCK(H&)
>         CALL RELEASERESOURCE(H&)       '<----
>         DEF DISPOSEH(H&)               '<----

You don't need to call DEF DISPOSEH.  ReleaseResource disposes of the
handle for you.

>       END IF
>       CALL CLOSERESFILE(resRef%)
>     END IF
>   END IF

Other than this, I don't see anything wrong, and I'm puzzled why it's
not working.  On the other hand, here's a routine that I've used
successfully to save resources to an arbitrary (perhaps brand-new)
file.  The main difference I see is that I'm calling USERESFILE.  That
_could_ make a difference: If you call OPENRESFILE (or OPENRFPERM, in my
case), but the res file happens already to be open, then (I seem to
recall) it returns the existing resRefNum for the file but does _not_
set the current resource file to that file (this flatly contradicts what
Inside Mac claims, so don't quote me!)

LOCAL FN SaveResources
  outFile$ = FILES$(_fSave, "Resource file:", "", vRefNum)
  LONG IF outFile$ <> ""
    resRefNum = FN OPENRFPERM(outFile$, vRefNum, _fsWrPerm)
    LONG IF resRefNum = -1
      'No resource file: create one
      dummy = FOLDER("", vRefNum)
      resRefNum = FN OPENRFPERM(outFile$, vRefNum, _fsWrPerm)
    END IF
    FOR i = 1 TO gSourceCount
      picHandle& = FN GetAPict&(i)
      LONG IF picHandle&
        id = FN UNIQUE1ID(_"PICT")
        SELECT CASE gSourceType
          CASE _ClipType
            '[prompt for resource name (null ok)]
          CASE ELSE
            resName$ = FN GetRootName$(gSource.namefield$(i))
        END SELECT
        CALL ADDRESOURCE(picHandle&, _"PICT", id, resName$)
        CALL WRITERESOURCE(picHandle&)
        CALL RELEASERESOURCE(picHandle&)
      END IF
    NEXT i