Michael G Schwern wrote:
> On Sun, Dec 17, 2000 at 12:11:01PM +1100, Jeremy Howard wrote:
> > Something to be careful of--it's easy to create a circular reference
when
> > using method pointers. As a result, neither the referrer nor referee
objects
> > are ever destroyed.
> >
> > When using method references it's important that your class keeps track
of
> > its references, and explicitly removes them when done to allow object
> > destruction to occur. Perhaps this could be incorporated into
> > Class::MethRef?
>
> Circular references make my brane hurt, but I don't think there's any
> circularlity here.  Once the method reference falls out of scope, so
> will its reference to the object.  Then the object will be garbage
> collected when it to falls out of scope.  The other way around, if the
> object falls out of scope first, the method reference will still work
> and the object will finally be destroyed when the meth ref goes away.
> The only snag is if you put the method reference on the object its
> refering to, that'll be circular.  I'll remember to document that caveat.
>
There's not necessarily any circular reference. The problem is that method
references are frequently used to implement event callbacks. This would
generally look something like this:
----
do {
my $a = new A;
my $b = new B;
$b->{on_click} = sub{$a->clicked($b);};
$b->do_click;
};
print "Out of scope\n";

package A;
sub clicked {my $self=shift;
  my $clicked_by = shift;
  print "I am clicked\n";
}
sub new {
  my $Proto = shift;
  my $Class = ref($Proto) || $Proto;
  my $Self = {};

  bless ($Self, $Class);
  return $Self;
}
sub DESTROY {print "bye A\n";}

package B;
sub do_click {
  my $self=shift;
  print "Clicking\n"; $self->{on_click}->();
}
sub new {
  my $Proto = shift;
  my $Class = ref($Proto) || $Proto;
  my $Self = {};

  bless ($Self, $Class);
  return $Self;
}
sub DESTROY {print "bye B\n";}
----

The problem is that in this case the destructors are not called immediately
after the objects go out of scope--this code prints:
----
Clicking
I am clicked
Out of scope
bye A
bye B
----

...which creates a memory leak in an event loop. The circular reference is
generally necessary, because the callback needs to know in what form the
click occurred (on object of class A in this case) and what widget was
clicked (an object of class B in this case). However, the circular reference
is not obvious at first glance--I've seen this kind of memory leak
frequently in mod_perl programs, for instance.

My objects that get method references always get an explicit destructor,
which removes all method references (which are tracked as they are created).
Something like this would be nice in a class that creates method
references--it would simply need to keep a list of referred objects, and
have an explicit destructor that iterates through the references and undefs
them. Of course, calling the destructor would be optional where no circular
reference exists.

Sorry this explanation isn't as clear as it should be--to many pints of
Guinness tonight ;-)


Reply via email to