8

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

  • 如果我将一个$guard变量取消定义为 sub 中的最后一条语句,则守卫的 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";

在代码中,我制作了自己的穷人Scope::GuardSGuard上面),只是为了使示例尽可能简单。您还可以使用Scope::Guard 并获得至少对我来说出乎意料的完全相同的结果。

我期望$mySubGuard内部mySub()应该首先被销毁$scopeGuard,调用的范围内mySub()应该最后被销毁。所以得到如下输出:

 shouldDestroyFirst
 mySub returned a undefined value
 shouldDestroyLast
 done

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

mySub returned a undefined value
shouldDestroyLast
shouldDestroyFirst
done

因此,看起来在外部作用域的本地变量被销毁后, $mySubGuardfrommySub()被销毁了。

为什么行为会因为我取消定义一个即将超出范围的变量而有所不同?为什么事后是否做某事很重要?

4

1 回答 1

1

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

此外,可能存在一个错误,因为您undef在“返回上下文”中使用它,因此存在正在检查某些内容的变量的副本。也许 Perl 保留了一个引用,它稍后会清理它和调用范围的结尾。

您还会注意到,在 -ing 守卫之后的任何其他语句都会undef导致预期的行为。包括 PBP 推荐的以return. Damian 的明确原因之一是它避免了意想不到的副作用。

问题解决了:就像undef守卫一样容易,你可以直接运行它的处理程序。不要将守卫取消定义为子的最后(隐式返回)语句。有理由显式取消定义守卫,例如您想立即运行它们的处理程序以进行进一步处理。

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

于 2012-07-11T13:20:53.877 回答