我开始注意到
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";
我在代码中创造了自己的穷人Scope::Guard
(上面的SGuard)
只是为了让例子尽可能简单.你也可以使用Scope::Guard
并得到完全相同的结果,至少对我来说意外.
我期待mySub()中的$mySubGuard应该首先销毁
并且应该销毁调用mySub()的范围中的$scopeGuard
持续.所以输出如下:
shouldDestroyFirst mySub returned a undefined value shouldDestroyLast done
如果我在mySub中使用undef $mySubGuard行,我会得到以上输出.
如果我不在mySub中使用undef $mySubGuard行,我会得到以下输出:
mySub returned a undefined value shouldDestroyLast shouldDestroyFirst done
所以,看起来mySub()的$mySubGuard被破坏了
在外部范围的本地变量被销毁之后.
为什么行为不同只是因为我取消了即将发生的变量
超出范围?为什么要做的事情至关重要
然后?
解决方法
它看起来像是某种优化.如果你取消变量并且之后不使用它,它会被放入某种队列以检查魔法或其他东西.但是,如果你之后做了什么,那么它就会成为DESTROY.
此外,可能存在一个错误,因为你在说“返回上下文”中没有它,所以有一个正在检查某个变量的变量的副本.也许Perl保留了一个引用,它随后会清理并调用范围的结束.
您还会注意到,在取消防守后,任何其他声明都会导致预期的行为.包括PBP建议结束所有潜艇的返回.达米安的一个显而易见的原因是它避免了意想不到的副作用.
问题解决了:就像取消防守一样容易,你可以直接运行它的处理程序.不要将undef guards作为sub的最后一个(隐式返回)语句.有理由明确地将undef守卫(例如你想要运行他们的处理程序)进行进一步处理.
这是令人困惑和意外的,但绝对不会在完成或标准化的代码中出现.