什么决定了类对象在PHP中何时被销毁?
假设我们有class什么决定了类对象在PHP中何时被销毁?,php,class,destructor,instantiation,Php,Class,Destructor,Instantiation,假设我们有classCFoo。在下面的示例中,何时调用CFoo::\uu destruct() function MyPHPFunc() { $foo = new CFoo(); . . . // When/where/how does $foo get destroyed/deleted? } 在本例中,当脚本退出MyPHPFunc的作用域时,是否会调用析构函数,因为$foo将不再可访问?信息位于中,尽管有些神秘: PHP 5引入了类似于其他面向对象语言(如C++)的析构函
CFoo
。在下面的示例中,何时调用CFoo::\uu destruct()
function MyPHPFunc()
{
$foo = new CFoo();
. . .
// When/where/how does $foo get destroyed/deleted?
}
在本例中,当脚本退出MyPHPFunc
的作用域时,是否会调用析构函数,因为$foo
将不再可访问?信息位于中,尽管有些神秘:
PHP 5引入了类似于其他面向对象语言(如C++)的析构函数概念。只要没有对特定对象的其他引用,或者在关闭序列期间以任何顺序调用析构函数方法
含义:当对象被销毁(=例如unset()
)或脚本关闭时,将调用析构函数
其他有用信息:
与构造函数一样,引擎不会隐式调用父析构函数。为了运行父析构函数,必须在析构函数体中显式调用parent::u destruct()
即使使用exit()停止脚本执行,也将调用析构函数。在析构函数中调用exit()将阻止其余关闭例程的执行
PHP5引入了一个类似于其他函数的析构函数概念
面向对象的语言,如C++。析构函数方法将是
在没有其他对某个特定对象的引用时立即调用
对象,或在关闭序列期间按任何顺序。
-
如果要查看正在运行的流程
在PHP中,所有值都保存在所谓的zval
s中。这些zval
s包含实际数据、类型信息以及参考计数(这对于您的问题很重要)。请查看以下代码段:
$a = new B; // $a points to zval(new B) with refcount=1
$b = $a; // $a, $b point to zval(new B) with refcount=2 (+1)
$c = $b; // $a, $b, $c point to zval(new B) with refcount=3 (+1)
unset($a); // $b, $c point to zval(new B) with refcount=2 (-1)
只要refcount
达到0
,就会释放zval
,并调用对象析构函数
以下是refcount
达到0
的一些示例:
unset
ing变量:
$a = new B; // refcount=1
unset($a); // refcount=0 => __destruct!
但是:
- 离开函数(或方法)范围
- 脚本执行结束
$a = new B; // refcount=1
die(); // refcount=0 => __destruct! (on script execution end all vars are freed)
// doesn't need to be die(), can be just normal execution end
这些显然不是导致refcount
减少的所有条件,而是您最常见的条件
我还应该提到,因为PHP5.3循环引用也会被检测到。因此,如果对象$a
引用对象$b
和$b
引用$a
,并且没有进一步引用$a
或$b
,则这两个对象的引用计数都将1
,但它们仍将被释放(和\u自毁)。在这种情况下,尽管破坏的顺序是未定义的行为。最好的了解方法是测试
然而,简单的答案是在垃圾清理期间调用u destruct。这对任何人都没有帮助,因为垃圾清理是一个正在进行的过程,当没有范围可以调用局部变量时,它会清理这些变量
不过,这里有一些示例代码和结果,完全解释了在脚本内部退出作用域时会发生什么
<?php
class testingdestructor {
public function __construct($num) {
$this->num = $num;
}
public function __destruct() {
echo "I am number {$this->num}\n";
}
}
class testing2{
public function __construct($num) {
$this->classtest = new testingdestructor($num);
}
public function __destruct() {
echo "I am not a number\n";
}
}
$iam1 = new testingdestructor(1);
$iam4 = new testing2(4);
function testfunction() {
$iam2 = new testingdestructor(2);
}
testfunction();
$iam3 = new testingdestructor(3);
unset($iam1);
这向我们展示了函数的结尾调用_destruct,就像unset一样,至少在实践中,脚本结束清理是按创建的相反顺序进行的。如果创建类的实例并使用对象。在完成所有任务后,如果调用析构函数并再次在下一行使用同一对象来执行某些其他任务,则将无法再使用。这意味着在脚本终止时(在关闭序列期间),将成功调用ur析构函数。。quote:“只要没有对特定对象的其他引用,或在关闭序列中以任何顺序调用析构函数方法”,因此当您不再使用它或脚本结束/它被终止时。因此,在我的示例中,当脚本退出MyPHPFunc
的范围时,是否会调用析构函数,因为$foo
将不再可访问?@Jim这是一个好问题,答案是-我不知道!(不过,你应该在你的代码前面写函数
,这样你问的问题就清楚了。)我猜它会在函数结束时被销毁——你必须尝试一下。我很想删除这个答案,因为当使用运行时用户创建的函数(create_函数)或在eval语句中运行代码时,垃圾收集可能会变得有点滑稽。例如:如果您在eval语句中声明了一个类及其方法(带有析构函数),那么就创建该类的全局实例。您将注意到析构函数从未被调用!这是因为当调用析构函数方法时,它已经在eval退出后被垃圾收集获取,因此该函数不再存在。(这可能是一个未记录的bug)@NikiC:谢谢。这是非常有用的。我所做的是创建函数(eval('classsomeclass{{uuuu desstructor(){some code}}}}}global$a;$a=newsomeclass();'))类似的东西…@NikiC,你能在答案中详细说明GC部分吗?那么,$a=new B$a=新的B在第二次分配$a
时,code>将看到B
析构函数的第一个实例?或者当GC运行/脚本结束时,它会破坏吗?
function a() {
$a = new B; // refcount=1
} // refcount=0 => __destruct! (as $a does not exist anymore)
$a = new B; // refcount=1
die(); // refcount=0 => __destruct! (on script execution end all vars are freed)
// doesn't need to be die(), can be just normal execution end
<?php
class testingdestructor {
public function __construct($num) {
$this->num = $num;
}
public function __destruct() {
echo "I am number {$this->num}\n";
}
}
class testing2{
public function __construct($num) {
$this->classtest = new testingdestructor($num);
}
public function __destruct() {
echo "I am not a number\n";
}
}
$iam1 = new testingdestructor(1);
$iam4 = new testing2(4);
function testfunction() {
$iam2 = new testingdestructor(2);
}
testfunction();
$iam3 = new testingdestructor(3);
unset($iam1);
I am number 2
I am number 1
I am number 3
I am not a number
I am number 4