If you intend to make use of the object returned from
tie
or tied
, and the class
defines a destructor, there is a subtle trap you must guard against.
Consider this (admittedly contrived) example of a class that uses a
file to log all values assigned to a scalar:
package Remember;sub TIESCALAR { my $class = shift; my $filename = shift; open(my $handle, ">", $filename) or die "Cannot open $filename: $! "; print $handle "The Start "; bless {FH => $handle, VALUE => 0}, $class; } sub FETCH { my $self = shift; return $self->{VALUE}; } sub STORE { my $self = shift; my $value = shift; my $handle = $self->{FH}; print $handle "$value "; $self->{VALUE} = $value; } sub DESTROY { my $self = shift; my $handle = $self->{FH}; print $handle "The End "; close $handle; } 1;
Here is an example that makes use of our
Remember
class:
use strict; use Remember; my $fred; $x = tie $fred, "Remember", "camel.log"; $fred = 1; $fred = 4; $fred = 5; untie $fred; system "cat camel.log";
This is the output when it is executed:
The Start 1 4 5 The End
So far, so good. Let's add an extra method to the
Remember
class that allows comments in the
file--say, something like this:
sub comment { my $self = shift; my $message = shift; print { $self->{FH} } $handle $message, " "; }
And here is the previous example, modified to use the
comment
method:
use strict; use Remember; my ($fred, $x); $x = tie $fred, "Remember", "camel.log"; $fred = 1; $fred = 4; comment $x "changing…"; $fred = 5; untie $fred; system "cat camel.log";
Now the file will be empty, which probably wasn't what
you intended. Here's why. Tying a variable associates it with the
object returned by the constructor. This object normally has only one
reference: the one hidden behind the tied variable itself. Calling
"untie
" breaks the association and eliminates that
reference. Since there are no remaining references to the object, the
DESTROY
method is triggered.
However, in the example above we stored a second reference to
the object tied to $x
. That means that after the
untie
there will still be a valid reference to the
object. DESTROY
won't get triggered, and the file
won't get flushed and closed. That's why there was no output: the
filehandle's buffer was still in memory. It won't hit the disk until
the program exits.
To detect this, you could use the
-w
command-line flag, or include the
use warnings "untie
" pragma in the current lexical
scope. Either technique would identify a call to
untie
while there were still references to the tied
object remaining. If so, Perl prints this warning:
untie attempted while 1 inner references still exist
To get the program to work properly and silence the warning,
eliminate any extra references to the tied object
before calling untie
. You can
do that explicitly:
undef $x; untie $fred;
Often though you can solve the problem simply by making sure your variables go out of scope at the appropriate time.