tags:

views:

61

answers:

4

I have a class which contains an array of objects and has methods to return an object from that array by reference. There is also a method to unset an object from the array.

However if I have a variable that references an object from the array, and that element is unset, the variable still has a reference to it. What do I need to do in the remove method that will destroy that object for good, including references to it.

class myClass
{
    public $objectList = array();

    public function __construct()
    {
        $objectList[] = new myObject();
    }

    public function &getObjectByReference()
    {
         return $this->objectList[0];
    }

    public function removeObject()
    {
        unset($this->objectList[0]);
    }
}
$myClass = new myClass();
$referencedObject = $myClass->getObjectByReference(); 
// this can now use the methods from myObject
$myClass-> removeObject(); 
// supposed to delete the object from memory. However $referencedObject is still
// able to use all the methods from myObject.

So thats the problem I am having, I need to be able to remove from the array and delete the object from memory so variables that reference that object are no longer usable.

A: 

Have you tried doing:

$referencedObject = &$myClass->getObjectByReference();

Is $referencedObject still there after putting in that &?

treeface
A: 

To create a reference from a return value both the function must return by reference (you already do so) and the assignment has to by by reference. So you should write:

$referencedObject =& $myClass->getObjectByReference();

If you do this the reference really will be destroyed.

But if you want to do destroy all variables having this object as value (and which are not references) then this is impossible. You can only remove the real references, not variables having the same value ;)

nikic
I tried doing it that way, but when calling print_r($referencedObject) it is still showing all of its member variables so it can't be gone?
wiggles
A: 

Php is working with garbage collector : if there is still a reference to the object then the object is not deleted.

unset($this->objectList[0])

Does not delete the object but the value in $this->objectList, the object still exists since he is referenced by $referencedObject.

One solution : when you delete the object, tell him he is being deleted and in that object you have a boolean "isDeleted". Then for every method of that object, check if isDeleted is true and in that case, just do nothing.

Loïc Février
A nice little work around, I hadn't thought of that :)
wiggles
A: 

This is the nature of PHP's garbage collector. To make sure a caller doesn't maintain a reference to your object you have to ensure they can never touch the original object. Here is an idea:

class myClass
{
    public $objectList = array();

    public function __construct()
    {
        $objectList[] = new Wrapper(new myObject());
    }

    public function getObject()
    {
         return $this->objectList[0];
    }

    public function removeObject()
    {
        $this->objectList[0]->emptyWrapper();
        unset($this->objectList[0]);
    }
}



class Wrapper {

    private $object;

    public function __construct($object) {
        $this->object = $object;
    }

    public function __call($method, $args) {
        return call_user_func_array(array($this->object, $method), $args);
    }

    public function __get($attr) {
        return $this->object->$attr;
    }

    public function __set($attr, $value) {
        $this->object->$attr = $value;
    }

    public function emptyWrapper() {
        $this->object = null;
    }

}

You can use this wrapper idea or you can use forced indirection and handles. I might prefer using forced indirection instead; otherwise, a caller can still keep the wrapper object alive - though it is fairly cheap.

class myClass
{
    public $objectList = array();

    public function __construct()
    {
        $objectList[] = new myObject();
    }

    public function getObjectHandle() {
        return 0;
    }

    public function removeObject($h)
    {
        unset($this->objectList[$h]);
    }

    public function call($h, $method, $args) {
        call_user_func(array($this->objectList[$h], $method), $args);
    }

    public function get($h, $attr) {
        return $this->objectList[$h]->$attr;
    }

    public function set($h, $attr, $value) {
        $this->objectList[$h]->$attr = $value;
    }

}

$myClass = new myClass();
$objectHandle = $myClass->getObjectHandle();
// example method call
$myClass->call($objectHandle, 'some_method', array('arg1', 'arg2')); 
$myClass->removeObject($objectHandle);
erisco