IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Lazarus Pascal Discussion :

Destruction de composant ne le ramenant pas à nil [Lazarus]


Sujet :

Lazarus Pascal

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 79
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 122
    Par défaut Destruction de composant ne le ramenant pas à nil
    Bonjour à toutes et à tous,

    Dans une application, je crée des "Panneau" qui sont des descendants de TPanel. Pour cela je crée un tableau de dimension 3.

    Pour créer un tableau, il suffit de cliquer sur la forme.

    Pour supprimer un tableau, il faut cliquer sur le tableau lui-même : on le voit disparaître. Pour autant, que j'utilise Destroy ou FreeAndNil, cette instance ne revient pas à nil. Je le vois car en cliquant à nouveau sur la forme, c'est un tableau à un nouvel indice qui se crée au lieu d'être à l'indice libéré.

    Voici le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    unit Unit1;
     
    {$mode objfpc}{$H+}
     
    interface
     
    uses
      Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls;
     
    type
     
      TPanneau = class(TPanel)
      public
        Index: Integer;
        Nom: String;
        procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
      end;
     
      { TForm1 }
     
      TForm1 = class(TForm)
        procedure FormClick(Sender: TObject);
      private
     
      public
        Panneau: array[1..3] of TPanneau;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.lfm}
     
    { TForm1 }
     
    procedure TForm1.FormClick(Sender: TObject);
    var
      i, j: Integer;
    begin
      j:= 0;
      for i:= 1 to 3 do
    //    if Panneau[i] = nil then
        if not Assigned(Panneau[i]) then
        begin
          j:= i;
          Break;
        end;
      if (j < 1) or (j > 3) then
        ShowMessage('Indice hors limite  j = '+ IntToStr(j))
      else
      begin
        Panneau[j]:= TPanneau.Create(Self);
        with Panneau[j] do
        begin
          Parent:= TWinControl(Self);
          Caption:= IntToStr(j);
          SetBounds(100, 60*i, 200, 30);
        end;
      end;
    end;
     
    procedure TPanneau.MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer);
    var
      i: Integer;
    begin
      i:= StrToInt(Caption);
      Destroy; Self:= nil;
    //  FreeAndNil(Self);
    end;
     
    end.
    Si vous avez une idée de ce qu'il faut faire pour que l'instance libérée revienne à nil.

    Cordialement.

    Pierre

  2. #2
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 929
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 929
    Par défaut
    • On n'invoque jamais le destructeur depuis une méthode de l'objet lui-même à moins d'être absolument certain d'être à son premier niveau d'appel. Si il veut se libérer lui-même il faut passer par une méthode asynchrone.
    • Dans ton code, comment l'objet TPanneau sait-il qu'il appartient au tableau Panneau[] ?


    Un exemple (parmi d'autres) pour une libération correcte (Delphi) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    type
      TSupprimerPanneauEvent = procedure(aPanneau :TObject) of object;
     
      TPanneau = class(TPanel)
      protected
        OnSupprimer :TSupprimerPanneauEvent;
        procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
      end;
     
      TForm1 = class(TForm)
        procedure FormClick(Sender: TObject);
      private
        procedure Supprimer(aPanneau :TObject);
      public
        Panneau: array[1..3] of TPanneau;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    procedure TForm1.FormClick(Sender: TObject);
    var
      i: integer;
    begin
      for i := Low(Panneau) to High(Panneau) do
        if not Assigned(Panneau[i]) then
        begin
          Panneau[i] := TPanneau.Create(Self);
     
          with Panneau[i] do
          begin
            Parent      := Self;
            Caption     := IntToStr(i);
            OnSupprimer := Supprimer;
            SetBounds(100, 60*i, 200, 30);
          end;
     
          Exit;
        end;
     
      ShowMessage('Tous les panneaux sont déjà utilisés.');
    end;
     
    procedure TForm1.Supprimer(aPanneau: TObject);
    var
      i: integer;
    begin
      for i := Low(Panneau) to High(Panneau) do
        if Panneau[i] = aPanneau then
        begin
          // Panneau[i] à nouveau utilisable
          Panneau[i] := nil;
          Break;
        end;
     
      // Destruction asynchrone
      TThread.ForceQueue(nil, procedure
                              begin
                                aPanneau.Free;
                              end);
    end;
     
    { TPanneau }
     
    procedure TPanneau.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    begin
      if Assigned(OnSupprimer) then
        OnSupprimer(Self);
    end;
     
    end.
    ps: la procedure anonyme devrait être supportée par FP, pas sûr pour la capture de variable.

  3. #3
    Membre émérite
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 79
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 122
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    - On n'invoque jamais le destructeur depuis une méthode de l'objet lui-même à moins d'être absolument certain d'être à son premier niveau d'appel. Si il veut se libérer lui-même il faut passer par une méthode asynchrone. ...
    Merci "Andnotor" pour cette proposition de code. Pour autant, deux problèmes de compilation :

    - Dans l'affectation de OnSupprimer, j'ai remplacé Supprimer par @Supprimer.
    - La destruction asynchrone :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    // Destruction asynchrone
      TThread.ForceQueue(nil, procedure
                              begin
                                aPanneau.Free;
                              end);
    me donne l'erreur suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Compilation du projet, Cible : project1.exe : Code de sortie 1, Erreurs : 2
    unit1.pas(2,27) Error: Illegal expression
    unit1.pas(3,27) Fatal: Syntax error, ")" expected but "BEGIN" found
    Citation Envoyé par Andnotor Voir le message
    - Dans ton code, comment l'objet TPanneau sait-il qu'il appartient au tableau Panneau[] ?
    Quand j'écris Panneau[j]:= TPanneau.Create(Self);, n'est-ce pas une preuve que le TPanneau appartient à la liste ?

    De mon côté, j'ai trouvé une solution pour que la suppression soit totale et remplacée par nil. Dans mon code, j'ai remplacé FreeAndNil(Self) par FreeAndNil(Form1.Panneau[i]);. Cela fonctionne, mais est-ce que cela n'induit pas un autre problème ?

    Cordialement.

    Pierre.

  4. #4
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 929
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 929
    Par défaut
    Citation Envoyé par ChPr Voir le message
    me donne l'erreur suivante :
    Ah dommage ! Il faudrait alors mémoriser le panneau à détruire et fournir une méthode à ForceQueue.

    Citation Envoyé par ChPr Voir le message
    Quand j'écris Panneau[j]:= TPanneau.Create(Self);, n'est-ce pas une preuve que le TPanneau appartient à la liste ?
    Il appartient à la liste mais n'en à pas conscience, il n'a lui-même aucune référence au tableau. Il sait qui est son propriétaire parce qu'il a un champ Owner, qui est son parent grâce au champ Parent mais là, rien !

    Citation Envoyé par ChPr Voir le message
    FreeAndNil(Form1.Panneau[i]);. Cela fonctionne, mais est-ce que cela n'induit pas un autre problème ?
    C'est pas mieux, voir ma première remarque : un objet ne se détruit jamais lui-même.

    Imagine Proc1 qui appelle Proc2, Proc2 libère l'objet ; que se passe-t-il lorsqu'on remonte à Proc1, méthode d'un objet qui n'existe plus ?

  5. #5
    Membre émérite
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 79
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 122
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Ah dommage ! Il faudrait alors mémoriser le panneau à détruire et fournir une méthode à ForceQueue. ...
    Il me semble que l'erreur indiquée montre plutôt un problème de syntaxe.

    Citation Envoyé par Andnotor Voir le message
    ... Il appartient à la liste mais n'en à pas conscience, il n'a lui-même aucune référence au tableau. Il sait qui est son propriétaire parce qu'il a un champ Owner, qui est son parent grâce au champ Parent mais là, rien ! ...
    En effet, si le tableau connaît ce qui le compose, Le panneau n'a pas de référence au tableau.

    Citation Envoyé par Andnotor Voir le message
    ...C'est pas mieux, voir ma première remarque : un objet ne se détruit jamais lui-même. ...
    Avant que ça fonctionne, j'avais un champ du panneau que j'affectais après son FreeAndNil : ça a tout de suite fait tilt ! J'ai déplacé ce champ avant la destruction pour que ça fonctionne. Je préférerais la solution que tu m'indiques, pour autant qu'on puisse régler ce problème de syntaxe et de procédure appelée.

    Cordialement.

    Pierre.

  6. #6
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 929
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 929
    Par défaut
    Citation Envoyé par ChPr Voir le message
    Il me semble que l'erreur indiquée montre plutôt un problème de syntaxe.
    Oui et ce problème se résout par... mémoriser l'objet à détruire et fournir une méthode à ForceQueue

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    type
      TSupprimerPanneauEvent = procedure(aPanneau :TObject) of object;
     
      { TPanneau }
     
      TPanneau = class(TPanel)
      protected
        OnSupprimer :TSupprimerPanneauEvent;
        procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
      end;
     
      { TForm1 }
     
      TForm1 = class(TForm)
        procedure FormClick(Sender: TObject);
      private
        ObjASupprimer :TObject;
        procedure DoSupprimer;
        procedure Supprimer(aPanneau :TObject);
      public
        Panneau: array[1..3] of TPanneau;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.lfm}
     
    { TForm1 }
     
    procedure TForm1.FormClick(Sender: TObject);
    var
      i: integer;
    begin
      for i := Low(Panneau) to High(Panneau) do
        if not Assigned(Panneau[i]) then
        begin
          Panneau[i] := TPanneau.Create(Self);
     
          with Panneau[i] do
          begin
            Parent      := Self;
            Caption     := IntToStr(i);
            OnSupprimer := @Supprimer;
            SetBounds(100, 60*i, 200, 30);
          end;
     
          Exit;
        end;
     
      ShowMessage('Tous les panneaux sont déjà utilisés.');
    end;
     
    procedure TForm1.DoSupprimer;
    begin
      if Assigned(ObjASupprimer) then
        FreeAndNil(ObjASupprimer);
    end;
     
    procedure TForm1.Supprimer(aPanneau: TObject);
    var
      i: integer;
    begin
      for i := Low(Panneau) to High(Panneau) do
        if Panneau[i] = aPanneau then
        begin
          // Panneau[i] à nouveau utilisable
          Panneau[i] := nil;
          Break;
        end;
     
      // Destruction asynchrone
      ObjASupprimer := aPanneau;
      TThread.ForceQueue(nil, @DoSupprimer);
    end;
     
    { TPanneau }
     
    procedure TPanneau.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    begin
      if Assigned(OnSupprimer) then
        OnSupprimer(Self);
    end;

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [D5] Composants RX : ca marche pas (rlink32)
    Par Oluha dans le forum Composants VCL
    Réponses: 15
    Dernier message: 11/04/2006, 15h06
  2. La Palette des composants ne s'affiche pas
    Par mouna201 dans le forum JBuilder
    Réponses: 1
    Dernier message: 02/02/2006, 15h27
  3. [VS2003] Un composant ActiveX ne peut pas créer un objet
    Par kendras dans le forum Framework .NET
    Réponses: 1
    Dernier message: 28/12/2005, 12h39
  4. JApplet avec J-Composants : compos. n'apparaissent pas
    Par gletare dans le forum Applets
    Réponses: 3
    Dernier message: 12/12/2005, 17h05
  5. problème de destruction de composants dynamique
    Par gandf dans le forum C++Builder
    Réponses: 4
    Dernier message: 23/08/2005, 10h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo