DESTROY以意想不到的顺序调用

Pet*_*rch 8 perl memory-management

我开始注意到一些奇怪的事情Scope::Guard.

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

代码也可以在这里找到

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";
Run Code Online (Sandbox Code Playgroud)

在代码中,我创建了自己的穷人Scope::Guard(SGuard上图),只是为了使示例尽可能简单.您也可以使用Scope::Guard 并获得至少对我来说意外的完全相同的结果.

我希望首先应该销毁$mySubGuard内部,mySub()并且最后应该销毁$scopeGuard调用的范围mySub().所以输出如下:

 shouldDestroyFirst
 mySub returned a undefined value
 shouldDestroyLast
 done
Run Code Online (Sandbox Code Playgroud)

如果我undef $mySubGuard在mySub中使用line,我会得到输出.如果我不在undef $mySubGuardmySub中使用line,我会得到以下输出:

mySub returned a undefined value
shouldDestroyLast
shouldDestroyFirst
done
Run Code Online (Sandbox Code Playgroud)

因此,它看起来像$mySubGuardmySub()地方到外部范围变量之后被销毁的销毁.

为什么行为有所不同只是因为我unf一个即将超出范围的变量?为什么事后做了什么事无关紧要?

Axe*_*man 1

看起来这是某种优化。如果你undef一个变量并且之后不使用它,它会被放入某种队列中以检查魔法或其他东西。但如果你事后做任何事情,它就会立即毁灭。

另外,可能存在一个错误,因为您是undef在“返回上下文”中对其进行操作,所以存在正在检查某些内容的变量的副本。也许 Perl 会保留一个稍后会清理的引用以及调用范围的末尾。

您还会注意到,在-ing 防护之后的任何其他语句都会导致预期的行为。undef包括 PBP 建议的所有字幕结尾return. 达米安明确的原因之一是它可以避免意想不到的副作用。

问题已解决:就像undef控制守卫一样简单,您可以直接运行其处理程序。不要将 undef 守卫作为子程序的最后一个(隐式返回)语句。有理由显式 undef 守卫,例如您想立即运行它们的处理程序,以执行以下操作进一步的处理。

这是令人困惑和意外的,但绝对不应该出现在成品或标准化代码中。