gpt4 book ai didi

Delphi - 使用接口(interface)内的接口(interface)时,我出现内存泄漏,但不知道为什么

转载 作者:行者123 更新时间:2023-12-03 07:53:56 26 4
gpt4 key购买 nike

我的目标是获得一个设置界面,其中包含我的应用程序设置信息。这可能是仅在应用程序运行时相关的信息以及应该是永久性的信息。为此,我认为在该应用程序设置界面中创建一个应用程序界面和一个持久设置界面是一个好主意,这样我就可以在 Ini 文件、数据库、加密文件等等之间切换。我认为代码有效。但是关闭 FastMM 后,TiniFile 中出现内存泄漏,我不知道为什么。这是带有类的接口(interface)单元。

unit UAPPSettings;

interface

uses
IniFiles;

type
IPersistentSettings = Interface
['{11542859-DEA3-4DBB-9A88-2068E407552C}']
function ReadString(Section, Name, Default: String): string;
procedure WriteString(Section, Name, Default: String);
function ReadPassword: string;
procedure WritePassword(Password: String);
end;

type
IAppSettings = Interface
['{559B8219-7D81-44CA-87A0-6D261B4A87E7}']
function GetPersistentSettings: IPersistentSettings;
property PersistentSettings: IPersistentSettings read GetPersistentSettings;
End;

type
TAppSettings = class(TInterfacedObject, IAppSettings)
strict private
FPersistentSettings: IPersistentSettings;
function GetPersistentSettings: IPersistentSettings;
procedure SetPersistentSettings(const Value: IPersistentSettings);
property PersistentSettings: IPersistentSettings read GetPersistentSettings write SetPersistentSettings;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
constructor create(aPersistentSettings: IPersistentSettings);
end;

type
TIniSettings = class(TInterfacedObject, IPersistentSettings)
strict private
FIniFile: TIniFile;
FfilePath: String;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
function ReadString(Section, Name, Default: String): string;
procedure WriteString(Section, Name, Default: String);
function ReadPassword: string;
procedure WritePassword(Password: string);
public
constructor create(FilePath: String);

end;

var
APPSettings: IAppSettings;

implementation

uses
FMX.Types;



{ TIniSettings }

constructor TIniSettings.create(FilePath: String);
begin
FfilePath := FilePath;
end;

function TIniSettings.ReadPassword: string;
begin
Result := ReadString('App', 'Passwort', '');
end;

function TIniSettings.ReadString(Section, Name, Default: String): string;
begin
If FIniFile.ValueExists(Section, Name) = false then
WriteString(Section, Name, Default);
Result := FIniFile.ReadString(Section, Name, Default);
end;

procedure TIniSettings.WritePassword(Password: string);
begin
WriteString('App', 'Passwort', Password);
end;

procedure TIniSettings.WriteString(Section, Name, Default: String);
begin
FIniFile.WriteString(Section, Name, Default);
end;

function TIniSettings._AddRef: Integer;
begin
log.d('IniSettings _AddRef');
FIniFile := TIniFile.create(FfilePath);
Result := inherited _AddRef;
end;

function TIniSettings._Release: Integer;
begin
log.d('IniSettings _Release');
Result := inherited _Release;
FIniFile.Free;
FIniFile := nil;
end;

{ TAppSettings }

constructor TAppSettings.create(aPersistentSettings: IPersistentSettings);
begin
FPersistentSettings := aPersistentSettings;
end;

function TAppSettings.GetPersistentSettings: IPersistentSettings;
begin
Result := FPersistentSettings;
end;

procedure TAppSettings.SetPersistentSettings(const Value: IPersistentSettings);
begin
FPersistentSettings := Value;
end;

function TAppSettings._AddRef: Integer;
begin
log.d('AppSettings _AddRef');
Result := inherited _AddRef;
end;

function TAppSettings._Release: Integer;
begin
log.d('AppSettings _Release');
Result := inherited _Release;
FPersistentSettings := nil;
end;

end.

这是我创建对象的方法:

APPSettings := TAppSettings.create(TIniSettings.create(System.IOUtils.TPath.GetDocumentsPath + System.SysUtils.PathDelim + 'config.ini'));

任何有关更好编码的建议都非常感谢

最佳答案

代码中的问题在于,您将 _AddRef_Release 方法用于调用这两个方法的实例的内存管理以外的目的。

以下代码是问题的原因:

function TIniSettings._AddRef: Integer;
begin
log.d('IniSettings _AddRef');
FIniFile := TIniFile.create(FfilePath);
Result := inherited _AddRef;
end;

function TIniSettings._Release: Integer;
begin
log.d('IniSettings _Release');
Result := inherited _Release;
FIniFile.Free;
FIniFile := nil;
end;

您正在 _AddRef 方法中构造 FIniFile 实例,这是错误的位置,并且 _Release 是错误的释放位置FNiFile.

_AddRef_Release 方法可以在实例生命周期内多次调用,并且每次调用 _AddRef 都会导致构造新的 TIniFile 实例。

为了正确的内存管理,这两个方法将被调用相同的次数,但_AddRef可能会连续调用多次,然后调用多个_Release根据代码,调用将在将来的某个时间进行。

换句话说,以下序列也是可能的:

_AddRef
_AddRef
_Release
_Release

_AddRef
_AddRef
_Release
_AddRef
_Release
_Release

当您调用 APPSettings := TAppSettings.create(TIniSettings.create(...) 时,第一个 _AddRef 将在将实例作为 aPersistentSettings 传递时被调用code> 参数,因为它没有声明为 const。然后,当将 aPersistentSettings 分配给 FPercientSettings 时,将调用下一个 _AddRef,这第二次调用将造成泄漏TIniFile 实例。

在构造函数调用结束时,_Release 调用将与 aPersistentSettings 参数 _AddRef 调用相匹配。这还将释放 ini 文件,并且 FIniFile 将为 nil 并且无法使用,直到您再次触发 FPercientSettings 实例上的引用计数。


您可能希望在 TIniSettings 构造函数中创建 FIniFile 并在其析构函数中销毁它,或者引入其他方法(Open/Close)如果您不想一直创建该 ini 文件并且您将在任何读取或写入操作中调用它们,则将其添加到将用于此目的的 TIniSettings 中。

当您从 _AddRef_Release 中删除与 ini 文件相关的代码时,您不再需要在类中实现它们,因为默认实现将完成这项工作。


仅当未在 _AddRef 中分配时,还有其他方法可以通过创建 TIniFile 来修复代码,但引用计数方法仍然是执行以下操作的错误位置您必须确保不会意外地在错误的位置触发额外的引用计数,这可能会导致您在可能仍然需要时得到 nil FIniFile 变量。

function TIniSettings._AddRef: Integer;
begin
log.d('IniSettings _AddRef');
if not Assigned(FIniFile) then
FIniFile := TIniFile.create(FfilePath);
Result := inherited _AddRef;
end;

关于Delphi - 使用接口(interface)内的接口(interface)时,我出现内存泄漏,但不知道为什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76513087/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com