Освобождение объектов с множественными ссылками - PullRequest
1 голос
/ 27 июня 2009

Это еще один пост о том, что я унаследовал приложение 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 ]

1 голос
/ 29 июня 2009

Если списки не являются владельцами, они не будут освобождать объекты при освобождении списка. Просто вызвать «Удалить» для каждого элемента тоже не удастся. Вам нужно будет перебрать список и вызвать Free для каждого элемента в списке, а затем освободить сам список.

Если вы сделаете владельцев списков, они сделают это для вас, когда вы освободите список.

for i := 0 to FAdapters.Count do Free(FAdapters[i]);
FreeAndNil(FAdapters);
0 голосов
/ 27 июня 2009

Вы понимаете, что можете распоряжаться объектами самостоятельно, не заставляя их владельцев автоматически распоряжаться ими? Я спрашиваю об этом, потому что такое чувство, что вы пытаетесь заставить автоматику выполнять работу во всех случаях.

...