Section 13.7. Weakening the Argument


13.7. Weakening the Argument

The %REGISTRY variable also holds a reference to each animal. So even if we toss away the containing variables, for instance by letting them go out of scope:

 {   my @cows = map Cow->named($_), qw(Bessie Gwen);   my @horses = map Horse->named($_), ('Trigger', 'Mr. Ed');   my @racehorses = RaceHorse->named('Billy Boy'); } print "We've seen:\n", map("  $_\n", Animal->registered); print "End of program.\n"; 

we'll still see the same result. The animals aren't destroyed, even though none of the code is holding the animals. At first glance, it looks like we can fix this by altering the destructor:

 ## in Animal sub DESTROY {   my $self = shift;   print '[', $self->name, " has died.]\n";   delete $REGISTRY{$self}; } ## this code is bad (see text) 

But this still results in the same output. Why? Because the destructor isn't called until the last reference is gone, but the last reference won't be destroyed until the destructor is called.[*]

[*] We'd make a reference to chickens and eggs, but that would introduce yet another derived class to Animal.

One solution for fairly recent Perl versions[] is to use weak references . A weak reference doesn't count as far as the reference counting, um, counts. It's best illustrated by example.

[] 5.6 and later.

The weak reference mechanism is built into the core of Perl version 5.8. We need an external interface for the weaken routine, though, which can be imported from the Scalar::Util module. In Perl 5.6, we can emulate the same function using the WeakRef CPAN module. After installing this module (if needed),[*] we can update the constructor as follows:

[*] See Chapter 3 for information on installing modules.

 ## in Animal use Scalar::Util qw(weaken); # in 5.8 and later use WeakRef qw(weaken);      # in 5.6 after CPAN installation sub named {   ref(my $class = shift) and croak 'class only';   my $name = shift;   my $self = { Name => $name, Color => $class->default_color };   bless $self, $class;   $REGISTRY{$self} = $self;   weaken($REGISTRY{$self});   $self; } 

When Perl counts the number of active references to a thingy,[] it wont count any that have been converted to weak references by weaken. If all ordinary references are gone, Perl deletes the thingy and turns any weak references to undef.

[] A thingy, as defined in Perls own documentation, is anything a reference points to, such as an object. If you are an especially boring person, you could call it a referent instead.

Now we'll get the right behavior for:

 my @horses = map Horse->named($_), ('Trigger', 'Mr. Ed'); print "alive before block:\n", map("  $_\n", Animal->registered); {   my @cows = map Cow->named($_), qw(Bessie Gwen);   my @racehorses = RaceHorse->named('Billy Boy');   print "alive inside block:\n", map("  $_\n", Animal->registered); } print "alive after block:\n", map("  $_\n", Animal->registered); print "End of program.\n"; 

This prints:

 alive before block:   a Horse named Trigger   a Horse named Mr. Ed alive inside block:   a RaceHorse named Billy Boy   a Cow named Gwen   a Horse named Trigger   a Horse named Mr. Ed   a Cow named Bessie [Billy Boy has died.] [Billy Boy has gone off to the glue factory.] [Gwen has died.] [Bessie has died.] alive after block:   a Horse named Trigger   a Horse named Mr. Ed End of program. [Mr. Ed has died.] [Mr. Ed has gone off to the glue factory.] [Trigger has died.] [Trigger has gone off to the glue factory.] 

Notice that the racehorses and cows die at the end of the block, but the ordinary horses die at the end of the program. Success!

Weak references can also solve some memory leak issues. For example, suppose an animal wanted to record its pedigree. The parents might want to hold references to all their children, while each child might want to hold references to each parent.

We can weaken one or the other (or even both) of these links. If we weaken the link to the child, Perl can destroy the child when all other references are lost, and the parent's link simply becomes undef (or we can set a destructor to completely remove it). However, a parent won't disappear as long as it still has offspring. Similarly, if the link to the parent is weakened, we'll simply get it as undef when the parent is no longer referenced by other data structures. It's really quite flexible.[*]

[*] When using weak references, always ensure you don't dereference a weakened reference that has turned to undef.

Without weakening, as soon as we create any parent-child relationship, both the parent and the child remain in memory until the final global destruction phase, regardless of the destruction of the other structures holding either the parent or the child.

Be aware though: use weak references carefully and don't just throw them at a problem of circular references. If you destroy data that is held by a weak reference before its time, you may have some very confusing programming problems to solve and debug.




Intermediate Perl
Intermediate Perl
ISBN: 0596102062
EAN: 2147483647
Year: N/A
Pages: 238

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net