Я уже писал про свой сборщик мусора в Delphi – класс TSafeObject и объект ProjectGarbageFinder в Delphi. Они выглядят примерно так:
Код: Выделить всё
TSafeObject=class
public
constructor Create;
destructor Destroy; override;
procedure SetThisObjectAsDebugging;//Не будет выводиться сообщение об ошибке
end;
constructor TSafeObject.Create;
begin
inherited;
if poGarbageFinderActive then begin
ProjectGarbageFinder.ObjectCreated(self);
end;
end;
destructor TSafeObject.Destroy;
begin
if poGarbageFinderActive then begin
ProjectGarbageFinder.ObjectDestroyed(self);
end;
inherited;
end;
Мне вначале говорили, что это не нужно, т.к. в Delphi есть опция ReportMemoryLeaksOnShutdown := True;
Но я убедился, что мой сборщик хорошо её дополняет, поскольку с ним можно подкрутить много надстроек, облегчающих отлов багов. Лучше всего было бы проанализировать стек на вызове TSafeObject.Create и разобраться, из какого участка кода эта функция была вызвана; но это я пока не осилил. И всё равно мой сборщик хорошо работает, поскольку пишет в отчёте разную вспомогательную информацию (скажем, время когда был вызван конструктор проблемного объекта).
Вначале я также думал, что можно включить ReportMemoryLeaksOnShutdown на постоянной основе и так и писать код, отлавливая утечки в зародыше. Но это, по-моему, тоже достаточно неудобно, по крайней мере для меня. Дело в том что часто нужно создавать временный код, который лень писать по правилам, без утечек. Так что я включаю свой сборщик мусора только время от времени, а в финале ещё включаю ReportMemoryLeaksOnShutdown – эта опция ловит оставшиеся утечки вроде неудалённых стандартных динамических массивов.
Также я почти отказался от обычных динамических массивов в Delphi, заменив их на такие штуки:
Код: Выделить всё
TRiAnySimpleArray<MyType> = record
public
FCount:integer;
FItems:tarray<mytype>;
procedure SetCount(newcount:integer); inline;
function GetItem(Index:Integer): MyType; inline;
procedure SetItem(Index:integer; value:mytype); inline;
property Item[index: integer]: MyType read getItem write setItem; default;
property Count:integer read FCount write SetCount;
procedure InitAndSetCount(newcount:integer);
procedure Initialize;
procedure Finalize;
end;
Код: Выделить всё
procedure TRiAnySimpleArray<MyType>.SetCount(newcount: integer);
begin
if poGarbageFinderActive then begin
if fcount>0 then ProjecTGarbageFinder.MemoryBlockDestroyed(addr(fitems[0]));
end;
setlength(fitems,newcount);
fcount:=newcount;
if poGarbageFinderActive then begin
if newcount>0 then ProjecTGarbageFinder.MemoryBlockCreated(addr(fitems[0]),newcount);
end;
end;