14.5. A Subtle Untying TrapIf 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: Here is an example that makes use of our Remember class:package Remember; sub TIESCALAR { my $class = shift; my $filename = shift; open(my $handle, ">", $filename) or die "Cannot open $filename: $!\n"; print $handle "The Start\n"; 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\n"; $self->{VALUE} = $value; } sub DESTROY { my $self = shift; my $handle = $self->{FH}; print $handle "The End\n"; close $handle; } 1; This is the output when it is executed:use strict; use Remember; my $fred; $x = tie $fred, "Remember", "camel.log"; $fred = 1; $fred = 4; $fred = 5; untie $fred; system "cat camel.log"; So far, so good. Let's add an extra method to the Remember class that allows comments in the file--say, something like this:The Start 1 4 5 The End And here is the previous example, modified to use the comment method:sub comment { my $self = shift; my $message = shift; print { $self->{FH} } $handle $message, "\n"; } 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.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"; 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: 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:untie attempted while 1 inner references still exist Often though you can solve the problem simply by making sure your variables go out of scope at the appropriate time.undef $x; untie $fred; Copyright © 2002 O'Reilly & Associates. All rights reserved. |
|