2009-01-27 6 views
4

J'ai une classe basée sur TInterfacedObject. Je l'ajoute à la propriété Data de TTreeNode.Pourquoi les descendants de TinterfacedObject ne sont-ils pas collectés?

TFacilityTreeItem=class(TInterfacedObject) 
private 
    m_guidItem:TGUID; 
    m_SomeOtherNode:TTreeNode; 
public 
end; 

i créer de nombreux cas de cet objet & avait supposé que parce qu'ils sont comptés référence, je ne devrais pas avoir besoin de les libérer. ce serait pratique.

Cependant, lors de la vérification, j'ai activé ReportMemoryLeaksOnShutdown et constaté qu'ils ne sont pas libérés après tout.

Ces objets sont créés dans un cadre placé sur le formulaire principal. dans FormClose du formulaire principal, j'efface les nœuds de l'arbre de sorte que chaque objet doit être libéré.

Qu'est-ce qui se passe?

je vous remercie pour votre aide!

Répondre

16

TInterfacedObject lui-même n'est pas compté par référence, seules les interfaces sont. Vous pouvez implémenter des interfaces à l'aide de TInterfacedObject, ce qui vous évite de mettre en œuvre vous-même les méthodes de comptage de références. Malheureusement, cela ne fonctionnera toujours pas dans votre cas: Le compilateur ne sait pas que vous assignez des interfaces à la propriété TTreeNode.Data car il n'est pas déclaré comme une interface mais comme un pointeur. Donc, toutes sortes de choses étranges se produiront:

MyInt := TFacilityTreeItem.Create; // ref count = 1 
// Node.Data := MyInt; // won't compile 
Node.Data := pointer(MyInt); // no interface assignment, ref count stays 1 
... 
end; // ref count reaches 0, your object gets freed 

Dès que vous essayez d'accéder à votre objet à travers la propriété .Data, vous obtiendrez une violation d'accès.

Donc, ne vous embêtez pas avec des interfaces dans ce cas, vous pourriez le faire fonctionner, mais ce sera beaucoup plus d'efforts que cela en vaut la peine.

+0

merci de votre réponse; Je vais utiliser TObject à la place. appris quelque chose de nouveau! –

3

Vous devez déclarer le champ/variable comme interface

IFacilityTreeItem = IInterface 
end; 

TFacilityTreeItem=class(TInterfacedObject, IFacilityTreeItem) 
private 
    m_guidItem:TGUID; 
    m_SomeOtherNode:TTreeNode; 
end; 

var 
    Item: IFacilityTreeItem; // Variable as Interface 
begin 
    Item:= TFacilityTreeItem.Create; 
... 
end; 

Pour accéder à vos champs, vous devez déclarer des propriétés dans l'interface IFacilityTreeItem, avec accesseurs et de mutateurs.

+0

merci pour votre réponse. Je ne suis pas heureux de faire les getters/setters donc je suppose que je vais le faire descendre de TObject à la place. –

+0

@Cesar Romero: S'il vous plaît modifier votre code, il ne serait jamais compiler (les interfaces ne peuvent pas avoir de membres de données, ni rien de privé). – mghie

+0

@mghie: Correction, merci. –

2

En tant que dummzeuch said, vous pouvez obtenir ceci pour travailler avec des interfaces, mais cela prend plus de code car la propriété Data d'un TTreeNode est un pointeur. Pour ceux qui se demandent comment faire cela, this link a un exemple de la façon de le faire pour TListItem (c'est à peu près la même chose pour TTreeNode). Vous pouvez également trouver utile de lire la section sur interfaces et la section suivante sur le comptage des références sur cette page.

+0

+1 pour l'astuce de lire la section sur les interfaces dans "Delphi in a Nutshell". C'est court mais précis, et il y a en général peu d'informations à avoir sur les interfaces dans Delphi. Certaines idées fausses circulent aussi. – mghie

Questions connexes