英文:
Delphi Memory Leak or Access Violation while using objects and interfaces
问题
The issue you're facing with memory management and access violations is related to the way you're handling object creation and memory. Delphi's memory management for interfaces can be a bit tricky. Here's a possible solution:
procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
var
LAbstractBaseDTO: TAbstractBaseDTO;
LAbstractBaseDTODuplicate: TAbstractBaseDTO;
begin
LAbstractBaseDTO := Self.FetchBusinessObject;
try
LAbstractBaseDTODuplicate := (LAbstractBaseDTO as IDuplicatable).CreateDuplicate;
Self.LoadData(LAbstractBaseDTODuplicate);
finally
LAbstractBaseDTO.Free; // Free the object that was created by FetchBusinessObject
end;
end;
In this modified code, we explicitly free the object returned by FetchBusinessObject
. This ensures that the object is properly disposed of when it's no longer needed, preventing memory leaks.
Make sure you handle memory management consistently throughout your code, ensuring that objects are freed when they're no longer in use. This should help you avoid memory leaks and access violations.
英文:
Currently I'm stuck a little bit regarding objects and interfaces and their memory management. I have a class which inherits from TInterfacedPersistent
, called TAbstractBaseDTO
. I also have an interface IDuplicatable
with a function function CreateDuplicate: TAbstractBaseDTO
.
I am using interfaces to achieve abstraction, not to manage memory and that is why I am using TInterfacedPersistent
as an ancestor class.
Unit.pas
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Actions, Vcl.ActnList,
Vcl.StdCtrls;
type
TAbstractBaseDTO = class abstract (TInterfacedPersistent)
public
constructor CreateEmpty; virtual; abstract;
end;
IDuplicatable = interface
['{153275DC-71C0-4CB6-B933-667419950C68}']
function CreateDuplicate: TAbstractBaseDTO;
end;
TCountryMasterDataDTO = class (TAbstractBaseDTO, IDuplicatable)
public
constructor CreateEmpty; override;
function CreateDuplicate: TAbstractBaseDTO;
end;
TBaseDataForm = class(TForm)
ActionList1: TActionList;
actDuplicate: TAction;
Button1: TButton;
procedure actDuplicateExecute(Sender: TObject);
strict private
var
FDataObject: TAbstractBaseDTO;
function FetchBusinessObject: TAbstractBaseDTO;
public
procedure LoadData(ADataObject: TAbstractBaseDTO);
destructor Destroy; override;
end;
var
BaseDataForm: TBaseDataForm;
implementation
{$R *.dfm}
procedure TBaseDataForm.LoadData(ADataObject: TAbstractBaseDTO);
begin
FDataObject.Free;
FDataObject := ADataObject;
end;
destructor TBaseDataForm.Destroy;
begin
FDataObject.Free;
inherited;
end;
constructor TCountryMasterDataDTO.CreateEmpty;
begin
Create;
end;
function TCountryMasterDataDTO.CreateDuplicate: TAbstractBaseDTO;
begin
Result := TCountryMasterDataDTO.Create;
end;
procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
var
LAbstractBaseDTO: IDuplicatable;
LAbstractBaseDTODuplicate: TAbstractBaseDTO;
begin
LAbstractBaseDTO := Self.FetchBusinessObject as IDuplicatable;
try
LAbstractBaseDTODuplicate := LAbstractBaseDTO.CreateDuplicate;
Self.LoadData(LAbstractBaseDTODuplicate);
finally
LAbstractBaseDTO := nil;
end;
end;
function TBaseDataForm.FetchBusinessObject: TAbstractBaseDTO;
begin
Result := TCountryMasterDataDTO.Create;
end;
end.
When I run actDuplicate
action FastMM4 reports a memory leak on shutdown.
However, changing the execute function to:
procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
var
LAbstractBaseDTO: TAbstractBaseDTO;
LAbstractBaseDTODuplicate: TAbstractBaseDTO;
begin
LAbstractBaseDTO := Self.FetchBusinessObject;
try
LAbstractBaseDTODuplicate := (LAbstractBaseDTO as IDuplicatable).CreateDuplicate;
Self.LoadData(LAbstractBaseDTODuplicate);
finally
LAbstractBaseDTO.Free;
end;
end;
will immediately raise an access violation, when running the action.
How can I solve this problem?
答案1
得分: 2
以下是您要翻译的内容:
问题出在您已经销毁的对象上存在着一个活跃的接口引用。
在这种情况下,当您将 LAbstractBaseDTO
强制类型转换为 IDuplicatable
时,编译器会创建一个隐藏的隐式接口引用。
LAbstractBaseDTODuplicate := (LAbstractBaseDTO as IDuplicatable).CreateDuplicate;
这个隐藏的接口引用将在方法的结尾由编译器清除,并且会调用已销毁对象实例上的 _Release
方法。
如果您在完整的调试模式下使用 FASTMM 运行代码,FASTMM 将检测到这种情况并显示错误消息,然后是堆栈跟踪:
FASTMM 检测到试图使用已释放对象的接口。为了中止当前操作,现在将引发访问冲突。
要解决这个问题,您需要将隐式接口引用转换为您控制的显式引用,然后在释放它引用的对象之前自行清除引用。
procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
var
LAbstractBaseDTO: TAbstractBaseDTO;
LAbstractBaseDTODuplicate: TAbstractBaseDTO;
LDuplicatable: IDuplicatable;
begin
LAbstractBaseDTO := FetchBusinessObject;
try
// 将接口引用保存到变量中
LDuplicatable := LAbstractBaseDTO as IDuplicatable;
LAbstractBaseDTODuplicate := LDuplicatable.CreateDuplicate;
LoadData(LAbstractBaseDTODuplicate);
finally
LDuplicatable := nil;
LAbstractBaseDTO.Free;
end;
end;
英文:
The problem you are experiencing is because there is alive interface reference to the object you have already destroyed.
In this case it is hidden implicit interface reference created by compiler when you typecasted LAbstractBaseDTO
as IDuplicatable
.
LAbstractBaseDTODuplicate := (LAbstractBaseDTO as IDuplicatable).CreateDuplicate;
This hidden interface reference will be cleared by compiler in the method epilogue and this will call _Release
method on the already destroyed object instance.
If you run your code with FASTMM in full debug mode, FASTMM will detect such scenario and show error message, followed by a stack trace:
FASTMM has detected an attempt to use an interface of a freed object. An access violation will now be raised in order to abort the current operation.
To solve that problem you need to conver that implicit interface reference to explicit one under your control and then you can clear that reference yoursel, before you free the object it references.
procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
var
LAbstractBaseDTO: TAbstractBaseDTO;
LAbstractBaseDTODuplicate: TAbstractBaseDTO;
LDuplicatable: IDuplicatable;
begin
LAbstractBaseDTO := FetchBusinessObject;
try
// save interface reference to a variable
LDuplicatable := LAbstractBaseDTO as IDuplicatable;
LAbstractBaseDTODuplicate := LDuplicatable.CreateDuplicate;
LoadData(LAbstractBaseDTODuplicate);
finally
LDuplicatable := nil;
LAbstractBaseDTO.Free;
end;
end;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论