Освобождение объектов с множественными ссылками
Это еще один пост о том, что я унаследовал приложение Intraweb с текстовым файлом утечек памяти объемом 2 МБ, как сообщал FastMM4, где я получил 115 экземпляров одного класса с утечкой 52 байта в каждом.
Утечки происходят из довольно запутанного создания экземпляров и обработки класса. Каждое создание экземпляра класса необходимо, чтобы приложение работало прямо сейчас. Так что я ищу способы клонировать класс с помощью простой очистки клона, или ссылаться по-другому, или..?
Первый экземпляр класса (TCwcBasicAdapter) представляет собой локальную переменную, которая добавляется в TObjectList (не Owning) и уничтожается с помощью TObjectList (FCDSAdapters):
procedure TCwcDeclaration.AttachAdapter(DS: TDataSource; const FormName, KeyFN, TitleFN: string; const Multiple: boolean = False;
const AllowAttachment: boolean = False; const AllowComment: boolean = False);
var
Forms : TCwcSessionForms;
Adapter: TCwcCDSAdapter;
KeyField, TitleField: TField;
begin
Forms := GetForms(FormName);
KeyField := DS.DataSet.FindField(KeyFN);
TitleField := DS.DataSet.FindField(TitleFN);
Adapter := TCwcBasicAdapter.Create(DS, KeyField, TitleField, Multiple);
Adapter.AttachDBPersist(Self.DBPersist);
Forms.AttachDataAdapter(Adapter);
Forms.SetAllowAttachments(AllowAttachment);
Forms.SetAllowComments(AllowComment);
end;
procedure TCwcSessionForms.AttachDataAdapter(aCDSAdapter: TCwcCDSAdapter);
var
Index: integer;
begin
if (FCDSAdapters.IndexOf(aCDSAdapter) -1)
then raise Exception.CreateFmt('Duplicate Adapter attempting to be attached on %0:s', [FFormClassName]);
Index := FCDSAdapters.Add(aCDSAdapter);
if (FDefaultAdapterIndex = -1)
then FDefaultAdapterIndex := Index;
end;
Второй экземпляр класса также является локальной переменной, которая добавляется в TObjectList (не Owning) и уничтожается с помощью TObjectList (FAdapters):
procedure TCwcCDSMulticastList.InitializeAdapters(const aSessionForms: TCwcSessionForms);
var
i, Count: integer;
Adapter: TCwcCDSAdapter;
TempMulticast: TCwcCDSEventMulticast;
begin
Count := aSessionForms.GetDataAdapterCount;
for i := 0 to Pred(Count) do begin
Adapter := aSessionForms.GetDataAdapter(i);
TempMulticast := FindDataSource(Adapter.DataSource);
if (TempMulticast = nil) then begin
TempMulticast := TCwcCDSEventMulticast.Create(Adapter.DataSource);
try
FMulticastList.Add(TempMulticast);
except
FreeAndNil(TempMulticast);
raise;
end;
end;
TempMulticast.AddObserver(Adapter);
FAdapters.Add(Adapter);
end;
end;
Третий экземпляр класса является частью шаблона наблюдателя из строки TempMulticast.AddObserver(Adapter) выше. Наблюдатель добавлен в TObjectList FObservers (Владение):
procedure TCwcCDSEventMulticast.AddObserver(const aCDSAdapter: TCwcCDSAdapter);
begin
FObservers.Add(TCwcCDSAdapterObserver.Create(aCDSAdapter));
end;
constructor TCwcCDSAdapterObserver.Create(const aCDSAdapter: TCwcCDSAdapter);
begin
inherited Create;
FOnStateChange := aCDSAdapter.OnStateChangeIntercept;
FOnAfterDelete := aCDSAdapter.AfterDeleteIntercept;
FInvalidateCursors := aCDSAdapter.InvalidateCursors;
end;
TCwcBasicAdapter протекает здесь, а не очищается при уничтожении FObservers.
Последнее, что я попробовал, это изменил FObservers на "Не принадлежащий", создав приватное поле для Адаптера, освободив приватное поле в TCwcCDSAdapterObserver.Destroy, но это вызывает ошибки.
Спасибо,
Пол Райс
2 ответа
Если списки не являются владельцами, они не будут освобождать объекты при освобождении списка. Просто вызвать "Удалить" для каждого элемента тоже не удастся. Вам нужно будет перебрать список и вызвать Free для каждого элемента в списке, а затем освободить сам список.
Если вы сделаете владельцев списков, они сделают это для вас, когда вы освободите список.
for i := 0 to FAdapters.Count do Free(FAdapters[i]);
FreeAndNil(FAdapters);
Вы понимаете, что можете распоряжаться объектами самостоятельно, не заставляя их владельцев автоматически распоряжаться ими? Я спрашиваю об этом, потому что кажется, что вы пытаетесь заставить автоматику делать работу во всех случаях.