Comment libérer un objet d'interface (Delphi 7)
Dans une partie de mon application, j'ai la situation où je reçois une interface qui je sais être un objet, mais je ne sais pas exactement de la classe. J'ai pour stocker un objet dans une interface de type variable.
Finalement, je pourrais recevoir d'une autre instance de ce type, et la première doit être jeté et remplacé par le nouveau. Pour cela, j'ai besoin de libérer de la mémoire qui la reliait objet utilise (mon interface fournit un AsObject méthode si je peux utiliser le TObject méthodes). Mon problème est que quand je veux attribuer "nil" à cette variable de nouveau, je reçois une violation d'accès.
J'ai écrit un petit programme qui reproduit ma situation. Je poste ici afin de clarifier la situation.
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
type
ISomeInterface = interface
function SomeFunction : String;
function AsObject : TObject;
end;
TSomeClass = class(TComponent, ISomeInterface)
public
called : Integer;
function SomeFunction : String;
function AsObject : TObject;
end;
var
SomeInterface : ISomeInterface;
i : Integer;
function TSomeClass.SomeFunction : String;
begin
Result := 'SomeFunction called!';
end;
function TSomeClass.AsObject : TObject;
begin
Result := Self;
end;
begin
try
SomeInterface := nil;
for i := 1 to 10 do
begin
if SomeInterface <> nil then
begin
SomeInterface.AsObject.Free;
SomeInterface := nil; // <-- Access Violation occurs here
end;
SomeInterface := TSomeClass.Create(nil);
SomeInterface.SomeFunction; // <-- if commented, Access
// Violation does not occur
end;
except on e : Exception do
WriteLn(e.Message);
end;
end.
La question est donc: comment puis-je libérer cet objet correctement?
source d'informationauteur Pablo Venturino
Vous devez vous connecter pour publier un commentaire.
En supposant que vous avez une raison légitime pour ce faire (et à l'aide de TComponent il est tout à fait possible que vous n' - voir à la fin de la réponse de pourquoi), alors le problème se produit en raison de la modification de la référence d'une interface variable après avoir détruit l'objet qu'il fait actuellement référence.
Toute modification de la référence de l'interface génère un code comme ceci:
devient (en termes simples):
Si vous relier à votre code, vous devriez voir le problème:
devient:
De sorte que vous pouvez voir qu'il est généré pour l'appel à la Release() qui résulte de l'attribution NÉANT à l'interface qui provoque une violation d'accès.
Vous devriez rapidement voir qu'il y a un moyen simple pour éviter cela, il suffit de reporter la libération de l'objet jusqu'à ce que après avoir NÉANT, la référence de l'interface:
MAIS
La question clé ici est pourquoi êtes-vous explicitement gratuit avec un objet qui est interfacé (et sans doute de référence pris en compte).
Lorsque vous modifiez le code pour le cache de l'objet de référence et de NÉANT l'interface avant explicitement Gratuit qui pratiquent l'objet, vous pouvez trouver que obj.Gratuit va alors provoquer une violation d'accès comme le NÉANT qui pratiquent de la référence de l'interface peut lui-même, l'objet étant libre.
La SEULE façon d'être sûr que explicitement gratuit avec la interfacé objet est sûr est:
1) Que le interfacé objet a remplacé/réimplémentée IUnknown et éliminé la référence compté gestion de durée de vie
ET
2) Que vous n'avez pas d'autres interfacé les références à cet objet ailleurs dans votre code.
Si la première de ces conditions n'a pas beaucoup de sens pour vous, alors, sans vouloir être condescendante, c'est probablement un bon signe que vous ne devriez pas être explicitement libérant l'objet comme il est presque certainement être géré par le nombre de références.
Ayant dit que, puisque vous utilisez un interfacé TComponent classe, puis aussi longtemps que votre TComponent classe n'a pas d'encapsuler un Objet COM, puis TComponent remplit la condition #1, donc tout ce qui reste alors, c'est que vous assurer que le reste de votre code répond à la condition #2.
Vous ne devez pas utiliser TComponent comme classe de base pour votre interfacé objets, vous devez utiliser TInterfacedObject à la place. TInerfacedObject a mis en œuvre les fonctions nécessaires pour gérer la durée de vie de gestion pour les interfaces en Delphi. Vous devez également ne jamais mélanger d'accéder à votre interface de l'interface et de l'objet. Voici une modification de votre code qui fonctionne très bien avec pas de fuites de mémoire.
Lorsque vous disposez d'une interface de variables telles que votre ISomeInterface var, vous n'avez pas besoin de le libérer, comme c'est le décompte de références et gratuit c'est l'auto lorsqu'il tombe hors de portée.
Lire Rob Kennedy, la réponse à cette question:
Delphi7, en passant interface de l'objet - cause de Pointeur non Valide l'Opération lors de la libération de l'objet
De http://delphi.about.com/od/beginners/l/aa113004a.htm
En cas de doute, essayez d'utiliser FastMM gestionnaire de mémoire et de réglage sur la mémoire de détection de fuite, pour voir si l'objet est une fuite.
Vous êtes de mélange. Tout dépend de
_AddRef
et_Release
méthodes. Vérifiez commentTInterfacedObject
danssystem.pas
est déclarée.Delphi appelle juste _AddRef et _Release méthodes de cours à l'aide de Interafaces, appel Gratuit dépend de l'objet implémente _Relase méthode.
TComponent
n'est pas automatiquement détruit (à l'exception de l'objet Com de composants).