2016-07-18 1 views
2

У меня есть следующий код:ТГогт в 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 лицо: Tahoma высота: -11

Почему это происходит и как я могу его решить?

EDIT

После ответа, я реализовал следующие решения (комментарии выделить ошибки, которые я получил:

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

посмотрите http://stackoverflow.com/questions/20386734/will-an-interface-implementing-form-free-itself-when-there-are-no-more-reference и http: // stackoverflow .com/questions/769329/bypassing-disabling-delphis-reference-counting-for-interfaces – RBA

+1

Передайте владельца в конструкторе, а не в nil –

ответ

4

Если вам нужна форма (или любой класс, который наследуется от TComponent) для обработки счетчиком ссылок на интерфейс, тогда вам нужно реализовать его самостоятельно (посмотрите на пример System.TInterfacedObject как пример того, как это сделать).

Вы в основном должны переопределять IInterface классу, который вы хотите включить подсчет ссылок на:

type 
    TInterfacedForm = class(TForm, IInterface) 
    // look at System.TInterfacedObject 
    end; 

Если вы делаете так, имейте в виду, что тогда не должны быть обработаны с помощью механизма владельца. Если вы зарегистрируете его в контейнере и используете его механизм создания по умолчанию, он будет передавать nil владельцу с Spring4D 1.2 - см. Spring.Container.Resolvers.TComponentOwnerResolver). В любой версии, прежде чем вам нужно явно создать ее с нулем внутри DelegateTo.

Если вы имеете дело с любыми элементами управления через интерфейсы, которые помещаются на другие элементы управления (например, фреймы) через их родительское свойство, помните также, что в этом случае вступает в действие другой механизм управления памятью, который может разрушить такой компонент, если его родитель становится разрушенным - если вы просто имеете дело с сопряженными формами, что не является проблемой, но я думал, что я упоминаю его здесь для полноты.

1

TComponent потомки (например, TForm) отключить интерфейсы подсчет ссылок, следовательно, никто не освобождает вторичную форму. Модель памяти основана на владельце, то есть когда освобожден родительский объект, который владеет объектом, он освобождает всех своих детей.

Таким образом, вы можете либо передать владельцу форму на функции заводской (возможно, Application или Application.MainForm) и придерживаться модели памяти ТСотропепЬ или добавить крюк на OnClose событие формы и установить Action в caFree. Первый будет уничтожать форму, когда приложение будет закрыто, и последнее уничтожит его, как только возможно, будет закрыта (как можно скорее)