为什么将这个引用对象设置为NULL不会重置它的所有副本?


Why does setting this referenced object to NULL not reset all copies of it?

当我们将NULL赋给$instance时,为什么$assigned不变为NULL ?当我们通过$instance->var改变$var的值时,$assigned获得新的更新值。

class SimpleClass
{
    // property declaration
    public $var = 'a default value';
    // method declaration
    public function displayVar() {
        echo $this->var;
    }
}
$instance   = new SimpleClass();
$assigned   =  $instance;
$reference  =& $instance;
$instance->var = '$assigned will have this value';
$instance = null; // $instance and $reference become null
var_dump($instance);
var_dump($reference);
var_dump($assigned);
输出:

NULL
NULL
object(SimpleClass)#1 (1) {
   ["var"]=>
     string(30) "$assigned will have this value"
}

基本知识

要理解发生了什么,你需要深入php的内部,看看内存是如何管理的。

在底层,值是这样表示的:

+--------------------+
| (type) {{ VALUE }} |
| refcount = a       |
| isref = b          |
+--------------------+

引用计数器a计算指向该值的变量的数量。isref标志显示该值是由值赋值(即$a = VALUE)还是由引用赋值(即$a =& VALUE)。

让我们假设如下情况:

                 +--------------------------+
$a ------------->| (int) 58                 |          $a = 58
                 | refcount = 1 ; isref = 0 |
                 +--------------------------+

如果你将$a的值复制到$b中,PHP只会增加refcount的值以节省内存(即不存储两次相同的值):

                 +--------------------------+
$a -------+----->| (int) 58                 |          $b = $a
          |      | refcount = 2 ; isref = 0 |
          |      +--------------------------+
          |
          |
$b -------+

如果您将$b的值更改为其他值,则会创建一个新值并减少对58的refcount:

                 +--------------------------+
$a ------------->| (int) 58                 |          $b = 'hello'
                 | refcount = 1 ; isref = 0 |
                 +--------------------------+
                 +--------------------------+
$a ------------->| (string) 'hello'         |
                 | refcount = 1 ; isref = 0 |
                 +--------------------------+

在引用的情况下,isref标志被设置为true,并且更改会影响两个标识符:

                 +--------------------------+
$a -------+----->| (int) 58                 |          $b =& $a
          |      | refcount = 2 ; isref = 1 |
          |      +--------------------------+
          |
          |
$b -------+

.

                 +--------------------------+
$a -------+----->| (int) 21                 |          $b = 21
          |      | refcount = 2 ; isref = 1 |
          |      +--------------------------+
          |
          |
$b -------+

关于对象引用

你可以经常听到在PHP中,对象通过引用透明地传递。根据医生的说法,这并不完全正确。名称标识符$a包含一个对象标识符,这就是函数之间传递的内容。我将在下面用一个类型为object的整数来说明这个标识符,我不知道它到底是如何在底层完成的,但是解释是成立的;-)

不要混淆标识符,即变量名与对象标识符,即对象在内存中的编号。

                 +--------------------------+
$a ------------->| (object) 547654764237685 |          $a = new FooBar();
                 | refcount = 1 ; isref = 0 |
                 +--------------------------+
在内存的其他地方,PHP存储了我们刚刚创建的标识符为547654764237685FooBar实例。当您使用值(object) 547654764237685时,PHP会自动检索该对象并允许您透明地使用它。

发生了什么?

让我们逐行检查你的代码。

首先创建一个新的SimpleClass实例。

                 +--------------------------+
$instance ------>| (object) 547654764237685 |          $instance = new SimpleClass();
                 | refcount = 1 ; isref = 0 |
                 +--------------------------+

然后给$assigned赋值,即增加refcount:

                 +--------------------------+
$instance ---+-->| (object) 547654764237685 |          $assigned = $instance;
             |   | refcount = 2 ; isref = 0 |
             |   +--------------------------+
             |
             |
$assigned ---+

第三行引用$reference$instance。因为值的refcount大于1,而且它还不是一个引用(isref = 0), PHP在内存中创建一个新的类似的值:

                 +--------------------------+
$instance ---+-->| (object) 547654764237685 |          $reference =& $instance;
             |   | refcount = 2 ; isref = 1 |
             |   +--------------------------+
             |
             |
$reference --+
                 +--------------------------+
$assigned ------>| (object) 547654764237685 |
                 | refcount = 1 ; isref = 0 |
                 +--------------------------+

三个标识符$instance$assigned$reference求值为完全相同的对象,因为对象标识符保持不变。但是,我们现在在内存中有两个不同的值。

这就是为什么调用$instance->var = '$assigned will have this value';会影响$instance->var$assigned->var$reference->var

现在,当您将$instance设置为null时,因为$reference引用相同的值,两者都受到影响:

                 +--------------------------+
$instance ---+-->| (NULL)                   |          $instance = null;
             |   | refcount = 2 ; isref = 1 |
             |   +--------------------------+
             |
             |
$reference --+
                 +--------------------------+
$assigned ------>| (object) 547654764237685 |
                 | refcount = 1 ; isref = 0 |
                 +--------------------------+

$assigned的对象引用不受影响,因为它是一个单独的值,因此$assigned仍然调用该对象。

我认为这是因为在将其标识符赋为空值后,对象本身仍然是完整的。

当你设置一个对象的字段-它驻留在对象的内存部分,但是当你设置它的"指针"另一个值-它驻留在其他地方,所以对象本身仍然在那里,可以被另一个标识符(指针)引用。

这里有一个ref: php.net