Delphi中使用对象和接口时的内存泄漏或访问冲突问题

huangapple go评论98阅读模式
英文:

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:

  1. procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
  2. var
  3. LAbstractBaseDTO: TAbstractBaseDTO;
  4. LAbstractBaseDTODuplicate: TAbstractBaseDTO;
  5. begin
  6. LAbstractBaseDTO := Self.FetchBusinessObject;
  7. try
  8. LAbstractBaseDTODuplicate := (LAbstractBaseDTO as IDuplicatable).CreateDuplicate;
  9. Self.LoadData(LAbstractBaseDTODuplicate);
  10. finally
  11. LAbstractBaseDTO.Free; // Free the object that was created by FetchBusinessObject
  12. end;
  13. 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

  1. unit Unit1;
  2. interface
  3. uses
  4. Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  5. Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Actions, Vcl.ActnList,
  6. Vcl.StdCtrls;
  7. type
  8. TAbstractBaseDTO = class abstract (TInterfacedPersistent)
  9. public
  10. constructor CreateEmpty; virtual; abstract;
  11. end;
  12. IDuplicatable = interface
  13. ['{153275DC-71C0-4CB6-B933-667419950C68}']
  14. function CreateDuplicate: TAbstractBaseDTO;
  15. end;
  16. TCountryMasterDataDTO = class (TAbstractBaseDTO, IDuplicatable)
  17. public
  18. constructor CreateEmpty; override;
  19. function CreateDuplicate: TAbstractBaseDTO;
  20. end;
  21. TBaseDataForm = class(TForm)
  22. ActionList1: TActionList;
  23. actDuplicate: TAction;
  24. Button1: TButton;
  25. procedure actDuplicateExecute(Sender: TObject);
  26. strict private
  27. var
  28. FDataObject: TAbstractBaseDTO;
  29. function FetchBusinessObject: TAbstractBaseDTO;
  30. public
  31. procedure LoadData(ADataObject: TAbstractBaseDTO);
  32. destructor Destroy; override;
  33. end;
  34. var
  35. BaseDataForm: TBaseDataForm;
  36. implementation
  37. {$R *.dfm}
  38. procedure TBaseDataForm.LoadData(ADataObject: TAbstractBaseDTO);
  39. begin
  40. FDataObject.Free;
  41. FDataObject := ADataObject;
  42. end;
  43. destructor TBaseDataForm.Destroy;
  44. begin
  45. FDataObject.Free;
  46. inherited;
  47. end;
  48. constructor TCountryMasterDataDTO.CreateEmpty;
  49. begin
  50. Create;
  51. end;
  52. function TCountryMasterDataDTO.CreateDuplicate: TAbstractBaseDTO;
  53. begin
  54. Result := TCountryMasterDataDTO.Create;
  55. end;
  56. procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
  57. var
  58. LAbstractBaseDTO: IDuplicatable;
  59. LAbstractBaseDTODuplicate: TAbstractBaseDTO;
  60. begin
  61. LAbstractBaseDTO := Self.FetchBusinessObject as IDuplicatable;
  62. try
  63. LAbstractBaseDTODuplicate := LAbstractBaseDTO.CreateDuplicate;
  64. Self.LoadData(LAbstractBaseDTODuplicate);
  65. finally
  66. LAbstractBaseDTO := nil;
  67. end;
  68. end;
  69. function TBaseDataForm.FetchBusinessObject: TAbstractBaseDTO;
  70. begin
  71. Result := TCountryMasterDataDTO.Create;
  72. end;
  73. end.

When I run actDuplicate action FastMM4 reports a memory leak on shutdown.

However, changing the execute function to:

  1. procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
  2. var
  3. LAbstractBaseDTO: TAbstractBaseDTO;
  4. LAbstractBaseDTODuplicate: TAbstractBaseDTO;
  5. begin
  6. LAbstractBaseDTO := Self.FetchBusinessObject;
  7. try
  8. LAbstractBaseDTODuplicate := (LAbstractBaseDTO as IDuplicatable).CreateDuplicate;
  9. Self.LoadData(LAbstractBaseDTODuplicate);
  10. finally
  11. LAbstractBaseDTO.Free;
  12. end;
  13. end;

will immediately raise an access violation, when running the action.

How can I solve this problem?

答案1

得分: 2

以下是您要翻译的内容:

问题出在您已经销毁的对象上存在着一个活跃的接口引用。

在这种情况下,当您将 LAbstractBaseDTO 强制类型转换为 IDuplicatable 时,编译器会创建一个隐藏的隐式接口引用。

  1. LAbstractBaseDTODuplicate := (LAbstractBaseDTO as IDuplicatable).CreateDuplicate;

这个隐藏的接口引用将在方法的结尾由编译器清除,并且会调用已销毁对象实例上的 _Release 方法。

如果您在完整的调试模式下使用 FASTMM 运行代码,FASTMM 将检测到这种情况并显示错误消息,然后是堆栈跟踪:

FASTMM 检测到试图使用已释放对象的接口。为了中止当前操作,现在将引发访问冲突。

要解决这个问题,您需要将隐式接口引用转换为您控制的显式引用,然后在释放它引用的对象之前自行清除引用。

  1. procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
  2. var
  3. LAbstractBaseDTO: TAbstractBaseDTO;
  4. LAbstractBaseDTODuplicate: TAbstractBaseDTO;
  5. LDuplicatable: IDuplicatable;
  6. begin
  7. LAbstractBaseDTO := FetchBusinessObject;
  8. try
  9. // 将接口引用保存到变量中
  10. LDuplicatable := LAbstractBaseDTO as IDuplicatable;
  11. LAbstractBaseDTODuplicate := LDuplicatable.CreateDuplicate;
  12. LoadData(LAbstractBaseDTODuplicate);
  13. finally
  14. LDuplicatable := nil;
  15. LAbstractBaseDTO.Free;
  16. end;
  17. 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.

  1. 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.

  1. procedure TBaseDataForm.actDuplicateExecute(Sender: TObject);
  2. var
  3. LAbstractBaseDTO: TAbstractBaseDTO;
  4. LAbstractBaseDTODuplicate: TAbstractBaseDTO;
  5. LDuplicatable: IDuplicatable;
  6. begin
  7. LAbstractBaseDTO := FetchBusinessObject;
  8. try
  9. // save interface reference to a variable
  10. LDuplicatable := LAbstractBaseDTO as IDuplicatable;
  11. LAbstractBaseDTODuplicate := LDuplicatable.CreateDuplicate;
  12. LoadData(LAbstractBaseDTODuplicate);
  13. finally
  14. LDuplicatable := nil;
  15. LAbstractBaseDTO.Free;
  16. end;
  17. end;

huangapple
  • 本文由 发表于 2023年4月10日 22:56:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75978177.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定