Это касается 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]:
TTestClass = class
strict private
[weak]fOnMyEvent:TAnonymProc;
public
property OnMyEvent: TAnonymProc read fOnMyEvent write fOnMyEvent;
end;
При присваивании объекта в переменную со слабой ссылкой не происходит увеличение счётчика ссылок объекта на единицу. Аналогично, при очистке слабой ссылки не происходит уменьшение счётчика объекта на единицу.
2. Не использовать анонимные методы, а использовать обычные указатели на метод:
Вместо TAnonymProc = reference to procedure; используем классический
TAnonymProc = procedure of object;
Демо пример, где можно отследить утечку прикрепил.
Полный код:
AnonMethodsCycle.zip