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