2015-03-15 2 views
4

j'ai cet exemple avec des fermetures imbriquées, ce qui démontre une fuite mémoirefermetures emboîtés et les variables capturées

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(); 

sortie sur mon perl est

awesome: barbie 
awesome: pony 
awesome: flash 
And you have surprise: pony 
destroying at /tmp/t.pl line 43 during global destruction. 

Et ce « lors de la destruction globale » signifie que l'objet couldn » t être détruit normalement, car il a quelques références circulaires.

Cependant, les seules références circulaires sont créés par

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

où nous utilisons auto à l'intérieur de la première fermeture $. Et plus tard dans make(), nous supprimerons ces étapes et les références circulaires. Mais ressemble à cette fermeture imbriquée avec "surprise" qui pose problème. Par exemple, si nous ne passons « poney » pour préparer() la production sera bonne comme prévu:

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

fermetures Alors, est niché dans perl capture les mêmes variables que la fermeture de niveau supérieur déjà capturé même si nous ne les utilisez pas?

Répondre

8

Perl utilisé pour sur-capturer dans les fermetures imbriquées avant, mais il ne le fait pas depuis 5.18.

$ 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. 
+0

Oh, ils ont brisé la compatibilité en arrière :) Bon à savoir! –

+0

@Oleg G, Pourquoi dites-vous réparer ce bug est une mauvaise chose? – ikegami

+0

non, je ne pense pas que ce soit mauvais. Au contraire, je pense que c'est le cas où la rétrocompatibilité devrait être rompue. Désolé pour tromper –