[ Team LiB ] |
10.6 Weakening the ArgumentThe %REGISTRY variable also holds a reference to each animal. So even if you toss away the containing variables: { 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"; you'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 you can fix this by altering the destructor: ## in Animal sub DESTROY { my $self = shift; print "[", $self->name, " has died.]\n"; delete $REGISTRY{$self}; } 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.[10]
One solution for fairly recent Perl versions[11] is to use weak references. A weak reference is a reference that doesn't count as far as the reference counting, uh, counts. It's best illustrated by example.
The weak reference mechanism is already built into the core of recent Perl versions, but as of this writing, the user interface is still accessed by a CPAN module called WeakRef. After installing this module,[12] you can update the constructor as follows:
## in Animal use WeakRef qw(weaken); ## new 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,[13] it won't 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.
Now you'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. One or the other (or even both) of these links can be weakened. If the link to the child is weakened, the child can be destroyed when all other references are lost, and the parent's link simply becomes undef (or you 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, you'll simply get it as undef when the parent is no longer referenced by other data structures. It's really quite flexible.[14]
Without weakening, as soon as any parent-child relationship is created, 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: weak references should be used carefully, not just thrown 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. |
[ Team LiB ] |