Это касается ARC компиляторов, Android, iOS и будущего Linux.
Если контейнер владелец, содержит классы, которые используют анонимный метод для общения с ним (классом владельцем), то такая конструкция порождает утечку памяти из-за появления циклической ссылки. Т.к. при присваивании анонимного метода инкрементируется счетчик ссылок и не меняется. Причем это не указано в хелпе.
А дело было так - при вызове MyCore.Free класс не уничтожался - не вызывался деструктор из за того, что после вызова Free, счетчик ссылок (reference Count) был равен 1. Приходилось пользоваться DisposeOf. Решил разобраться.
Для этого перекрыл виртуальные методы TObject отвечающие за изменения счетчика объекта (см. также полный пример ниже).
function __ObjAddRef: Integer; override;
function __ObjRelease: Integer; override;
Итак TCore содержит класс TTestClass - у которого есть событие OnMyEvent.
Прототип описан как анонимная процедура - TAnonymProc = reference to procedure;
При указании анонимной процедуры, т.е. :
procedure TCore.SetEvent;
begin
fTest.OnMyEvent:= procedure ()
begin
fSetFlag :=true;
end;
end;
счетчик ссылок TCore увеличивается на 1 и не изменяется при выходе из SetEvent.
Теперь при вызове Free TCore - не будет вызван деструктор, который должен уничтожит классы TCore и TTestClass и произойдет утечка памяти.
Решение : 1. Использовать слабые ссылки - weak, в нашем примере добавить атрибут [weak]:
При присваивании объекта в переменную со слабой ссылкой не происходит увеличение счётчика ссылок объекта на единицу. Аналогично, при очистке слабой ссылки не происходит уменьшение счётчика объекта на единицу.
2. Не использовать анонимные методы, а использовать обычные указатели на метод:
Вместо TAnonymProc = reference to procedure; используем классический TAnonymProc = procedure of object;
Демо пример, где можно отследить утечку прикрепил.
Полный код:
Спойлер
unit Unit1;
interface
uses
System.SysUtils,System.Types,System.UITypes,System.Classes,System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls;
type
TAnonymProc= reference to procedure;TTestClass=class
strict private
fOnMyEvent:TAnonymProc;public
property OnMyEvent:TAnonymProc read fOnMyEvent write fOnMyEvent;
end;TCore=class(TObject)
strict private
fTest:TTestClass;
fSetFlag: boolean;public
constructor Create;
destructor Destroy; override;
procedure SetEvent;function __ObjAddRef:Integer; override;function __ObjRelease:Integer; override;
end;TForm1=class(TForm)Button1:TButton;Button2:TButton;
procedure Button1Click(Sender:TObject);
procedure Button2Click(Sender:TObject);private
fCore:TCore;public{Public declarations }
end;varForm1:TForm1;
implementation
{$R *.fmx}
procedure TForm1.Button2Click(Sender:TObject);
begin
fCore :=TCore.Create;
fCore.SetEvent;
end;
procedure TForm1.Button1Click(Sender:TObject);
begin
Tag:= fCore.RefCount;
fCore.Free;
end;{TCore}
constructor TCore.Create;
begin
fTest :=TTestClass.Create;
end;
destructor TCore.Destroy;
begin
fTest.Free;
inherited;
end;
procedure TCore.SetEvent;
begin
fTest.OnMyEvent:= procedure ()
begin
fSetFlag :=true;
end;
end;// test it on ARC compilers like Android, iOSfunctionTCore.__ObjAddRef:Integer;
begin
Result:= inherited __ObjAddRef;
end;functionTCore.__ObjRelease:Integer;
begin
Result:= inherited __ObjRelease;
end;
end.
Вопрос
ENERGY
Это касается ARC компиляторов, Android, iOS и будущего Linux.
Если контейнер владелец, содержит классы, которые используют анонимный метод для общения с ним (классом владельцем), то такая конструкция порождает утечку памяти из-за появления циклической ссылки. Т.к. при присваивании анонимного метода инкрементируется счетчик ссылок и не меняется. Причем это не указано в хелпе.
А дело было так - при вызове MyCore.Free класс не уничтожался - не вызывался деструктор из за того, что после вызова Free, счетчик ссылок (reference Count) был равен 1. Приходилось пользоваться DisposeOf. Решил разобраться.
Для этого перекрыл виртуальные методы TObject отвечающие за изменения счетчика объекта (см. также полный пример ниже).
function __ObjAddRef: Integer; override;
function __ObjRelease: Integer; override;
Итак TCore содержит класс TTestClass - у которого есть событие OnMyEvent.
Прототип описан как анонимная процедура - TAnonymProc = reference to procedure;
При указании анонимной процедуры, т.е. :
счетчик ссылок TCore увеличивается на 1 и не изменяется при выходе из SetEvent.
Теперь при вызове Free TCore - не будет вызван деструктор, который должен уничтожит классы TCore и TTestClass и произойдет утечка памяти.
Решение :
1. Использовать слабые ссылки - weak, в нашем примере добавить атрибут [weak]:
При присваивании объекта в переменную со слабой ссылкой не происходит увеличение счётчика ссылок объекта на единицу. Аналогично, при очистке слабой ссылки не происходит уменьшение счётчика объекта на единицу.
2. Не использовать анонимные методы, а использовать обычные указатели на метод:
Вместо TAnonymProc = reference to procedure; используем классический
TAnonymProc = procedure of object;
Демо пример, где можно отследить утечку прикрепил.
Полный код:
AnonMethodsCycle.zip
Изменено пользователем ENRGYСсылка на комментарий
2 ответа на этот вопрос
Рекомендуемые сообщения
Присоединяйтесь к обсуждению
Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.