嵌套闭包和捕获变量

4

我有一个带有嵌套闭包的示例,可以演示内存泄漏问题。

use v5.10;
use strict;

package Awesome;

sub new {
    bless {steps => [], surprise => undef}, shift;
}

sub say {
    print "awesome: ", $_[1], "\n";
}

sub prepare {
    my ($self, @steps) = @_;

    for my $s (@steps) {
        push @{$self->{steps}}, sub {
            $self->say($s);

            if ($s eq 'pony') {
                $self->{surprise} = sub {
                    $s;
                }
            }
        };
    }
}

sub make {
    my $self = shift;

    while (my $step = shift @{$self->{steps}}) {
        $step->();
    }

    if ($self->{surprise}) {
        printf("And you have surprise: %s\n", $self->{surprise}->());
    }
}

sub DESTROY {
    warn "destroying";
}

package main;

my $a = Awesome->new;
$a->prepare('barbie','pony','flash');
$a->make();

我的Perl输出结果是:
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
destroying at /tmp/t.pl line 43 during global destruction.

“在全局销毁期间”意味着对象无法以正常方式被销毁,因为它具有某些循环引用。

然而,唯一创建循环引用的是

push @{$self->{steps}}, sub {
            $self->say($s);

我们在第一个闭包里使用了 $self。然后在 make() 中,我们将删除这些步骤和循环引用。但是看起来这个带有“惊喜”的嵌套闭包会出现问题。例如,如果我们不将“pony”传递给 prepare(),输出结果将不如预期。

awesome: barbie
awesome: flash
destroying at /tmp/t.pl line 43.

因此,在Perl中,即使我们没有使用它们,嵌套闭包也会捕获与来自上一级的闭包已经捕获的相同变量吗?
1个回答

8

从5.18版本开始,Perl不再在嵌套的闭包中过度捕获变量。

$ tail -n 9 a.pl   # Modified to make clearer when the object is destroyed.
package main;

{
   my $a = Awesome->new;
   $a->prepare('barbie','pony','flash');
   $a->make();
}

print "done.\n";

$ 5.16.3t/bin/perl a.pl
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
done.
destroying at a.pl line 43 during global destruction.

$ 5.18.2t/bin/perl a.pl
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
destroying at a.pl line 43.
done.

哦,他们破坏了向后兼容性 :) 知道就好! - Oleg G
@Oleg G,为什么你说修复这个 Bug 是一件坏事? - ikegami
不,我不认为这是坏事。相反地,我认为这是应该打破向后兼容性的情况。对于误导,我感到抱歉。 - Oleg G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接