[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

Re: [MacPerl] Mac::Dialogs questions



Alan Fry writes:
|Chris Nandor wrote Date: Sat, 28 Feb 1998 16:18:21 -0500:
|>I set the following in your module:
|>  DESTROY {print $dlg;dispose $dlg}
|>  END {print $dlg;dispose $dlg}
|>When run with END, it worked.  When run with DESTROY, it printed the right
|>reference information, but did not dispose.  I don't know why.  Maybe since
|>the data is not held in the object, but is a file-wide lexical, the data
|>that $dlg refers to had been destroyed before the destructor was called, so
|>all that's left is a pointer and no data?

Yes, I think this is the case.

|Trying this today with 'SimpleStatus.pm' I found Brian's suggestion does
|indeed work perfectly. In this module the MacDialog *is* my'ed in new().

Actually being my'ed in the new method isn't the important part. What
makes it work in this case is that the dialog is saved in the object's
hash. Remember that perl uses simple reference counts. If you use a
global and *don't* save the dialog reference in the object's hash,
then the reference count on the dialog ref is 1. At cleanup time, perl
decrements it, sees it's 0, and disposes of the reference. Later during
cleanup time when DESTROY is called, the reference has already been
disposed of. However, if you save the reference in the object's hash,
the dialogs ref's reference count is now 2 (1 for the global, 1 for the
object hash). During cleanup, perl decrements the count on the reference
from the global, but this only makes it 1, so it isn't disposed of. When
DESTROY is called, the reference is still valid and the dialog can be
disposed.

What this means is that either of these would work:

# global
use vars qw($dlg);

sub new () { $pkg = shift; ...; $dlg = new MacDialog ...;
	my $ref = {}; $ref->{dlg} = $dlg; bless $ref, $pkg; }
sub close () { my $dlg = $_[0]->{dlg}; dispose $dlg; }
sub DESTROY () { $_[0]->close; }

or:

# lexical
sub new () { $pkg = shift; ...; my $dlg = new MacDialog ...;
	my $ref = {}; $ref->{dlg} = $dlg; bless $ref, $pkg; }
sub close () { my $dlg = $_[0]->{dlg}; dispose $dlg; }
sub DESTROY () { $_[0]->close; }

The second's a little more bullet-proof, because in the first you
could accidentally use the global $dlg, which may be the wrong one
if you have multiple dialogs. In the second, the only way to get
at the dialog reference is through its object, so you'll always get
the right one (unless someone's been doing some nasty stuff to the
objects. :-))

|        sub DESTROY { print "at destroy $_[0]\\n"; $_[0]->close }
|or      END { print "at end $_[0]\n"; $_[0]->close }
|you find that '$_[0]' is not seen at all in the END{} block. It is however
|seen in the DESTROY{} sub-routine [...].

This makes sense. DESTROY is an object method, which means it's always
called with the blessed object as its first (and in DESTROY's case, only)
argument. In fact, you can call DESTROY just like any other object method:

$obj = new Object;
$obj->foo;
$obj->bar;
$obj->DESTROY;

(As an aside having little to do with the discussion at hand, you *have*
to call DESTROY yourself if you have a self-referential data structure
and you want it cleaned up properly. Reference counts fail in this case.)

On the other hand, END is a global block, so it doesn't get called with
any arguments. Any value you want to be visible in an END block needs to
be a global or file scope variable.

|It is odd that DESTROY seems not to work with the 'global $dlg' but perhaps
|that is how it is *designed* to be -- it simply doesn't expect to see any
|'global' variables, and wouldn't know what to do with one if it did. Is
|that it I wonder?

It sees them, it's just that by the time DESTROY is called, the global $dlg
has been disposed of by perl's cleanup mechanism. By saving $dlg in the
object's hash reference, perl sees that it's still in use and doesn't
dispose of the global $dlg, so it's still around when DESTROY is called.

To bring this back to MacPerl a bit, any time you are allocating some
resource that perl doesn't know how to dispose of, it's a good idea to
wrap it in an object, make the reference to the resource an instance
variable of the object, and provide a DESTROY method which uses the
instance variable to properly dispose of the resource.

Although the above is true on all versions of perl, it's particularly
important in MacPerl, because any Mac resource you allocate, such as
GrafPorts, windows, dialogs, resources, memory with NewHandle/NewPtr,
etc., are all things perl hasn't the slightest clue about, so all
should be wrapped in an object so they can be disposed of properly
even if the user aborts the script.

|My sincere apologies to Brian for even thinking of suggesting he was wrong
|when he knew exactly what should be done all along.

No problem. We were just working from different initial conditions.
DESTROY works, but only if things are set up in a certain way first.

Brian

***** Want to unsubscribe from this list?
***** Send mail with body "unsubscribe" to mac-perl-request@iis.ee.ethz.ch