Re: Is there an advantage to storing references to objects in an attribute instead of the object itself?
by haukex (Archbishop) on Dec 02, 2017 at 11:02 UTC
|
Is there an advantage to storing the large object as a reference to the large object instead of the object itself?
An object in Perl is just an anonymous data structure (it just happens to be blessed) that is referred to by one or more references, so in code that uses the object you'd never really store "the object itself" in a variable, but only a reference. You are free to take as many references to the same object as you like*, but in regards to "storing the object itself", I don't understand the question, and I think a Short, Self-Contained, Correct Example would help.
* However, if you have your "one big object" storing references to your "smaller objects", and your smaller objects referring back to the big one, you've created circular references that will prevent the objects from being garbage-collected, in other words, your program has a memory leak. One way to deal with this is to weaken one of the references on one end with weaken from Scalar::Util. Note that even code like you showed in the reply here will still mean there are circular references.
In the following code, try commenting out the line weaken($self->{big_obj}); and see how the output changes to show the memory leak.
use warnings;
use strict;
{
package Big;
sub new { my $class = shift; bless {@_}, $class }
sub new_little {
my ($self,$id) = @_;
push @{$self->{little_objs}},
Little->new(id=>$id, big_obj=>$self);
return $self;
}
sub DESTROY
{ print "DESTROY ",__PACKAGE__," id=",shift->{id},"\n" }
}
{
package Little;
use Scalar::Util qw/weaken/;
sub new {
my $class = shift;
my $self = {@_};
weaken($self->{big_obj});
return bless $self, $class;
}
sub DESTROY
{ print "DESTROY ",__PACKAGE__," id=",shift->{id},"\n" }
}
my $big1 = Big->new(id=>"One")->new_little("Hello");
print "Clearing \$big1...\n";
$big1 = undef; # no more references to object should cause GC
my $big2 = Big->new(id=>"Two")->new_little("World");
print "Clearing \$big2...\n";
$big2 = undef;
END { print "END\n" }
Minor edits for clarity. | [reply] [d/l] [select] |
|
|
OK, I ran your code. Very interesting. Thanks for illustrating this problem for me so clearly. Circular references are one of those concepts I blew off and didn't worry about. It's probably time I started worrying.
So what's the best solution to my problem? Here's my self-contained code that illustrates what I'm doing:
#! /usr/bin/env perl
use warnings;
use strict;
package BigObjs;
use Moose;
has 'little_objs' => ( is => 'rw', isa => 'HashRef', default => sub {
+{} } );
# our reader method
sub get_little_obj {
my ($s, $id) = @_;
return $s->little_objs->{$id};
}
# add new little objects to our collection
sub add_little_obj {
my ($s, $little_obj) = @_;
# create code ref to reader method in big object
$little_obj->get_obj_by_id(sub{$s->get_little_obj(@_)});
$s->little_objs->{$little_obj->id} = $little_obj;
}
package LittleObjs;
use Moose;
has 'id' => (is => 'rw');
has 'data' => (is => 'rw');
has 'get_obj_by_id' => (is => 'rw');
sub alter_another_little_object {
my ($s, $id) = @_;
my $other_little_object = $s->get_obj_by_id->($id);
$other_little_object->do_stuff;
}
sub do_stuff {
my $s = shift;
print "I'm doing stuff to: " . $s->id . "\n";
}
no Moose;
my $big = BigObjs->new();
my $little1 = LittleObjs->new(id => 'little1', data => 'foo');
my $little2 = LittleObjs->new(id => 'little2', data => 'bar');
$big->add_little_obj($little1);
$big->add_little_obj($little2);
$little1->alter_another_little_object('little2');
So you are saying I need to use sub{$s->get_little_obj(@_)} as an argument to a weaken function?
| [reply] [d/l] [select] |
|
|
To reply to your various posts in this thread in one place:
Circular references are one of those concepts I blew off and didn't worry about. It's probably time I started worrying.
It's definitely something to keep in mind when building object trees like you are. I tried out some different ways to locate circular references in this node, but applying that to your code just shows a bunch of circular references of Class::MOP::* objects, so I'd guess Moose is wrapping everything up and hiding the actual circular references. Applying it to my code above shows them a little more clearly.
I stored a reference to a reader method ... it provides encapsulation
Personally, I don't think hiding the "big object" in a code ref adds much. Consider that the class for the big object should already provide all the necessary encapsulation, and so I think there's nothing wrong with simply providing a getter on the "little object" class that returns the big object. You might just consider making this field read-only.
... the weak_ref => 1 attribute property. However, slapping that in breaks my code.
Yes, I can confirm that in your code putting weak_ref on either the little_objs or get_obj_by_id causes "Can't use an undefined value as a HASH/subroutine reference" errors. Also, if I take your code as shown here, and add a sub DESTROY to track destruction like I showed above, it shows the memory leak. Since adding a level of indirection with the code ref doesn't seem to break my code above, at the moment I'm guessing that it's the combination of Moose's weak_ref and the code ref that is causing the problem, but I haven't investigated this further, because I can fix the problem by doing what I said above, just giving the little object a reference to the big object:
| [reply] [d/l] [select] |
|
|
I'd probably just store a weak reference to the big object directly in the little one, and use it in alter_another_little_object to get the other object:
#! /usr/bin/perl
use warnings;
use strict;
{ package BigObjs;
use Moose;
has 'little_objs' => (is => 'rw', isa => 'HashRef', default => sub
+ { {} });
sub get_little_obj {
my ($s, $id) = @_;
return $s->little_objs->{$id};
}
sub add_little_obj {
my ($s, $little_obj) = @_;
$little_obj->big_obj($s);
$s->little_objs->{$little_obj->id} = $little_obj;
}
}
{ package LittleObjs;
use Moose;
has id => (is => 'rw');
has data => (is => 'rw');
has big_obj => (is => 'rw', weak_ref => 1);
sub alter_another_little_object {
my ($s, $id) = @_;
my $other_little_object = $s->big_obj->get_little_obj($id);
$other_little_object->do_stuff;
}
sub do_stuff {
my $s = shift;
print "I'm doing stuff to: " . $s->id . "\n";
}
}
my $big = BigObjs->new();
my $little1 = LittleObjs->new(id => 'little1', data => 'foo');
my $little2 = LittleObjs->new(id => 'little2', data => 'bar');
$big->add_little_obj($little1);
$big->add_little_obj($little2);
$little1->alter_another_little_object('little2');
warn "Done\n";
($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord
}map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
|
|
| [reply] [d/l] |
Re: Is there an advantage to storing references to objects in an attribute instead of the object itself?
by dsheroh (Monsignor) on Dec 02, 2017 at 08:48 UTC
|
Erm... Objects are references in Perl. If you're distinguishing between an object and a reference to an object, than that's the difference between a reference to (usually) a hash and a reference to a reference to a hash. Maybe there's something weird going on in your use case that you haven't mentioned, but, in general, there's no benefit to storing a reference to a reference instead of the original reference itself. | [reply] |
Re: Is there an advantage to storing references to objects in an attribute instead of the object itself?
by nysus (Parson) on Dec 02, 2017 at 06:28 UTC
|
So I did something different. Instead of storing the large object in an attribute, I stored a reference to a reader method I created in the large object:
use Moose;
my $little_obj = Class->new( { get_big_obj_data => sub{ $big_object->g
+et_data(@_) } } );
# retrieve data like this:
my $data = $little_obj->get_big_obj_data->(@args);
Seems like this is a much smarter thing to do because it provides encapsulation.
| [reply] [d/l] |
Re: Is there an advantage to storing references to objects in an attribute instead of the object itself?
by Anonymous Monk on Dec 02, 2017 at 08:04 UTC
|
So you have a god object? Nah, not it ... What do you got? Class attributes always struck me as static-y things.. :/ | [reply] |
| A reply falls below the community's threshold of quality. You may see it by logging in. |