DESTROY以意想不到的顺序调用

时间:2012-07-11 07:04:20

标签: perl memory-management

我开始注意到Scope::Guard的奇怪之处。

  • 如果我将$guard变量取消作为sub中的最后一个语句,则后卫将获得该变量 比我预期的要晚。
  • 如果我没有取消它,或者我做了什么 在undef $guard之后(任何事情),当引用消失时它会被调用 记录范围。我想知道为什么。

还可以找到代码here

my $sClass = 'SGuard';
# Uncomment to use Scope::Guard instead:
# use Scope::Guard; $sClass = 'Scope::Guard';

package SGuard;
sub new {
    my ($class, $sub) = @_;
    return bless { sub => $sub }, $class;
}

sub DESTROY {
    my ($self) = @_;
    $self->{sub}->();
}

package main;
sub mySub {
    my $mySubGuard = $sClass->new(sub { 
         print "shouldDestroyFirst\n" 
    });

    # Do something - any no-op will do.
    undef;

    # Comment out this line and it works
    undef $mySubGuard;

    # Or uncomment the next undef line and it works. Any statement(s) or
    # no-ops will do but we test the return value of mySub to make sure it
    # doesn't return a reference, so undef...

    # undef;
}
{
    my $scopeGuard = $sClass->new(sub { 
        print "shouldDestroyLast\n" 
    });

    # Check that mySub returns undef to ensure the reference *did* go out
    # of scope
    printf "mySub returned a %sdefined value\n", defined mySub() ? "" : "un";
}
print "done\n";

在代码中,我创造了自己的穷人Scope::GuardSGuard以上) 只是为了让例子尽可能简单。您也可以使用Scope::Guard 并得到完全相同的结果,至少对我来说意外。

我希望首先销毁$mySubGuard内的mySub() 并且应该销毁调用$scopeGuard的范围中的mySub() 持续。所以输出如下:

 shouldDestroyFirst
 mySub returned a undefined value
 shouldDestroyLast
 done

如果我在mySub中使用undef $mySubGuard行,我会得到以上输出。 如果我在mySub中没有使用undef $mySubGuard行,我会得到以下输出:

mySub returned a undefined value
shouldDestroyLast
shouldDestroyFirst
done

因此,看起来$mySubGuard的{​​{1}}被销毁了 在外部范围的本地变量被销毁之后。

为什么行为不同只是因为我取消了即将发生的变量 超出范围?为什么要做的事情很重要 事后?

1 个答案:

答案 0 :(得分:1)

看起来它是某种优化。如果你undef变量并且之后不使用它,它会被放入某种队列以检查魔法或其他东西。但是如果你之后做了什么,它就会在那时完成DESTROY。

此外,可能存在一个错误,因为你是undef - 在说“返回上下文”中,有一个正在检查某个变量的变量的副本。也许Perl保留了一个引用,它随后会清理并调用范围的结束。

您还会注意到undef后面的任何其他语句会导致预期的行为。包括PBP建议以return结束所有潜艇。达米安的一个显而易见的原因是它避免了意想不到的副作用。

问题解决了:像undef一样轻松地守卫,你可以直接运行它的处理程序。不要将undef guards作为sub的最后一个(隐式返回)语句。有理由明确表示不守护,例如你想立即运行他们的处理程序,进行进一步处理。

令人困惑和意外,但绝对不会在完成或标准化的代码中出现。

相关问题