【问题标题】:Unset all instances of an object PHP取消设置对象 PHP 的所有实例
【发布时间】:2014-01-29 01:54:53
【问题描述】:

我有一个类,它有一个返回自身新实例的方法。 这允许我使用一个对象,而不必一遍又一遍地声明具有相同值的新对象。对于那些想知道为什么不扩展课程或其他东西的人,它只是让我的生活更轻松,它就像一种魅力,但我的最终目的是将它们全部删除。

例如:

$foo = new myClass('name1','ab','cd');
$bar = $foo->duplicate();

//duplicate() saves the new object in itself in an array:
array_push($this->instance,new myClass('name1','ab','cd'));
//and returns a pointer to the saved instance.
return end($this->instance);

$foo$bar 现在共享相同的方法和值,但这些值可以单独修改。

$foo->changeName('newName'); 

$bar->changeName('newName2');

我的问题是,如果我取消设置创建的第一个类unset($foo),PHP 会自动取消设置其他实例($bar),还是垃圾收集器最终会删除它们?

我已经通过使 $foo 不稳定来测试它,并且在我调用 $foo 时给我一个错误,但在我调用 $bar 时却没有。

我要做的是一次取消设置该类的所有实例。

谢谢。

【问题讨论】:

  • 如果您想确定性地销毁对象的“集合”,请将它们保存在某种真正的集合中,然后unset 一次将它们全部(循环)。

标签: php object unset


【解决方案1】:

根据我对 PHP 参考的理解:

http://www.php.net/manual/en/features.gc.refcounting-basics.php

销毁主实例不会自动销毁所有其他实例,因为它们最终不会指向同一个“zval”。

但是,如果不再引用它们,垃圾收集最终会销毁所有实例。由于在您的示例中 $bar 仍然引用第二个实例,因此它不会被销毁。

如果你想同时取消所有设置,你可以做什么:

  1. 使用引用对象的所有实例的静态数组
  2. 每次创建新对象时,在类的静态数组中添加对该对象的引用
  3. 使用静态函数 unsetAll() 循环遍历此数组并一一取消设置所有实例

【讨论】:

  • 我忘了说所有实例都保存在主对象中,并返回一个指向保存实例的指针。
  • 我不确定,正如我之前提到的,在取消主对象后调用新实例时它不会给我一个错误。看看我的问题,我添加了更具体的代码示例。谢谢!
  • 我的解释能力不好,很差,为了更清楚而编辑了答案
【解决方案2】:

尝试以下代码清除所有 php 对象。

public function clearAllVars() 
   { 
      $vars = get_object_vars($this); 
      foreach($vars as $key => $val) 
      { 
         $this->$key = null; 
      } 
   } 
} 

【讨论】:

    【解决方案3】:

    自动清理?

    不,PHP 不了解您的架构。它不会删除“级联”对象,它们是独立的实体——而且,例如,它们可以属于不同的范围。简单代码:

    class Test
    {
       protected $id = null;
       protected $uniqid = null;
    
       public function __construct($id)
       {
          $this->id = $id;//user-passed variable
          $this->uniqid = uniqid();//internal: to identify instance 
       }
    
       public function getCopy()
       {
          return new self($this->id);
       }
    
       public function getIdentity()
       {
          return $this->uniqid;
       }
    }
    
    $foo = new Test(3);
    $bar = $foo->getCopy();
    var_dump($foo->getIdentity());//valid
    
    unset($foo);
    //still valid: bar has nothing to do with foo
    var_dump($bar->getIdentity());
    

    顺便说一句,您可以在 PHP 中使用 clone 进行复制(不过,这显然会导致对象克隆)

    简单的方法

    解决问题的最简单方法是遍历$GLOBALS,并使用instanceof 进行检查。这有一个严重的弱点:内部函数/方法范围不会受到影响:

    //static since doesn't belong to any instance:
    public static function cleanup()
    {
       foreach($GLOBALS as $name=>$var)
       {
          if($var instanceof self)
          {
             unset($GLOBALS[$name]);
          }
       }
    }
    

    -和

    $foo = new Test(3);
    $bar = $foo->getCopy();
    var_dump($foo->getIdentity(), $bar->getIdentity());//valid
    Test::cleanup();
    //2 x notice:
    var_dump($foo, $bar);
    

    注意,这与“子”机制无关(即,它将清除全局范围内的所有实例 - 无论从哪个复制而来)。

    常见情况

    上面的示例不会在常见情况下做这些事情。为什么?想象一下,您将拥有持有者类:

    class Holder
    {
       protected $obj = null;
    
       public function __construct($obj)
       {
          $this->obj = $obj;
       }
    
       public function getData()
       {
          return $this->obj;
       }
    }
    

    然后你会将实例传递给它:

    $foo = new Test(3);
    $bar = $foo->getCopy();
    $baz = new Holder($bar);
    

    -那么在普通情况下,即使是这种简单的情况,您也没有机会处理。而对于更复杂的情况,你也会陷入困境。

    怎么办?

    我建议:在需要时明确销毁对象。隐式取消设置是一种副作用,即使你会以某种方式维护它(我可以想象观察者模式 + 一些全局注册表) - 这将是可怕的副作用,这会破坏你的代码的可读性。代码也是如此,使用我上面写的$GLOBALS - 我不建议在任何情况下这样做。

    【讨论】:

    • 非常感谢,这看起来非常有用且解释清楚,快速的问题,只是因为我想保留我到目前为止所做的事情,如果不是我明白这是不可能的:如果我返回一个传递引用或指针? &$this->实例?显然,如果这有效 Holder($bar) 将返回一个错误,但我们的想法是要小心并知道我们在做什么。谢谢!
    • 它仍然不可靠,因为您无法确定类/函数内部发生了什么。可能是以某种方式复制数据? (例如,通过clone)。在通常情况下,您会失去跟踪 - 这是肯定的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 2014-05-13
    • 2013-01-31
    • 2018-07-20
    • 2016-08-03
    • 1970-01-01
    相关资源
    最近更新 更多