Управление TForm в Spring4D
У меня есть следующий код:
Project.dpr
program Project2;
uses
madExcept,
madLinkDisAsm,
madListHardware,
madListProcesses,
madListModules,
Spring.Container,
Vcl.Forms,
uRegistrations in '..\Memory leak II\uRegistrations.pas',
Unit3 in 'Unit3.pas' {MainForm},
Unit4 in 'Unit4.pas' {SecondaryForm},
Unit5 in 'Unit5.pas';
{$R *.res}
begin
RegisterTypes(GlobalContainer);
Application.Initialize;
Application.MainFormOnTaskbar := True;
// MainForm:=TMainForm.Create(nil);
Application.CreateForm(TMainForm, MainForm);
MainForm.SecondaryForm := Globalcontainer.Resolve<ISecondaryForm>;
Application.Run;
end.
uRegistrations.pas, который регистрирует интерфейс
unit uRegistrations;
interface
uses
Spring.Container;
procedure RegisterTypes(Container: TContainer);
implementation
uses
Unit5,
Unit4;
procedure RegisterTypes(Container: TContainer);
begin
container.RegisterType<ISecondaryForm, TSecondaryForm>.DelegateTo(
function: TSecondaryForm
begin
result := TSecondaryForm.Create(nil);
end);
Container.Build;
end;
end.
Unit3.pas держит основную форму
unit Unit3;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Unit5,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs;
type
TMainForm = class(TForm)
private
{ Private declarations }
FSecondaryForm: ISecondaryForm;
public
{ Public declarations }
property SecondaryForm: ISecondaryForm read FSecondaryForm write FSecondaryForm;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
end.
Unit4.pas со вторичной формой
unit Unit4;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
unit5,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TSecondaryForm = class(TForm, ISecondaryForm)
private
{ Private declarations }
public
{ Public declarations }
end;
//var
// SecondaryForm: TSecondaryForm;
implementation
{$R *.dfm}
end.
и, наконец, Unit5.pas с объявлением интерфейса
{$M+}
unit Unit5;
interface
type
ISecondaryForm=interface
['{62D63E9A-A3AD-435B-8938-9528E70D78B1}']
end;
implementation
end.
Он компилируется и запускается регулярно, но когда я закрываю приложение, у меня три утечки памяти.
номер выделения: 8482 время выполнения программы: 721 мс тип: дескриптор кисти: $461027f5 стиль: BS_SOLID цвет: $f0f0f0
номер выделения: 8318 время работы программы: 697 мс тип: TSecondaryForm адрес: $d51ac64 размер: 924 права доступа: чтение / запись
номер выделения: 8267 время запуска программы: 693 мс тип: дескриптор дескриптора шрифта: $1d0a28f1 грань: высота тахомы: -11
Почему это происходит и как я могу решить это?
РЕДАКТИРОВАТЬ
После ответа я реализовал следующие решения (в комментариях отмечены ошибки, которые я получил:
procedure RegisterTypes(Container: TContainer);
begin
container.RegisterType<ISecondaryForm, TSecondaryForm>.DelegateTo(
function: TSecondaryForm
begin
result := TSecondaryForm.Create(nil);
result.Owner:=Application.MainForm;//cannot assign to a read-only property
result.Parent:=Application; //incompatible types
result.Parent:=application.MainForm;//memory leak
end);
Container.Build;
end;
Я также попытался изменить метод OnClose для TSecondaryForm следующим образом:
procedure TSecondaryForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree; //memory leak
end;
но у меня утечка памяти.
Что я делаю не так со всей техникой выше?
В конце я просто сделал два метода _AddRef и _Release для управления подсчетом ссылок, как это было предложено в комментариях, и у меня больше нет утечек памяти.
TSecondaryForm = class(TForm, ISecondaryForm)
private
{ Private declarations }
protected
FRefCount: Integer;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
{ Public declarations }
end;
function TSecondaryForm._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;
function TSecondaryForm._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result=0 then
self.Free;
end
2 ответа
Если вы хотите, чтобы форма (или любой класс, унаследованный от TComponent) была обработана путем подсчета ссылок интерфейса, вам нужно реализовать ее самостоятельно (посмотрите на System.TInterfacedObject
как пример как это сделать).
Вам в основном нужно переопределить IInterface
к классу, на который вы хотите включить подсчет ссылок:
type
TInterfacedForm = class(TForm, IInterface)
// look at System.TInterfacedObject
end;
Если вы делаете это, имейте в виду, что это не должно обрабатываться механизмом владельца. Если вы зарегистрируете его в контейнере и используете его механизм создания по умолчанию, он передаст nil владельцу начиная с Spring4D 1.2 - см. Spring.Container.Resolvers.TComponentOwnerResolver
). В любой версии, прежде чем вам нужно явно создать его с нуля внутри DelegateTo
,
Если вы имеете дело с какими-либо элементами управления интерфейсами, которые помещаются в другие элементы управления (например, фреймы) через их родительское свойство, имейте в виду, что в таком случае вступает в действие другой механизм управления памятью, который может уничтожить такой компонент, если его родительский элемент будет уничтожен. - если вы просто имеете дело с сопряженными формами, это не проблема, но я подумал, что упомяну это здесь для полноты.
TComponent
потомки (как TForm
) отключить подсчет ссылок на интерфейсы, поэтому никто не освобождает вторичную форму. Модель памяти основана на владельце, то есть, когда освобождается родительский объект, которому принадлежит объект, освобождаются все его дочерние элементы.
Таким образом, вы можете передать владельца в форму на заводской функции (возможно, Application
, или же Application.MainForm
) и придерживаться модели памяти TComponent или добавить хук на OnClose
событие формы и множества Action
в caFree
, Первый уничтожит форму при закрытии приложения, а второй уничтожит ее, как только будет закрыта вторичная форма (как можно скорее).