среда, 17 августа 2011 г.

FreeAndNil, Assigned and Destructor (Delphi)

FreeAndNil и зачем он нужен
Каждый объект в Delphi имеет свой жизненный цикл.
Обычно он сводится к:
  • созданию объекта
  • манипуляциям с объектом
  • удалению объекта
При этом, объявляя переменную объекта класса мы объявляем указатель на объект данного класса
Например выражение MyList: TList; объявляет указатель с именем MyList - это все равно что мы бы написали MyList: Pointer;, с тем лишь отличием, что в первом случае указатель будет типизированным.


Эти объявленные указатели могут использоваться повторно:
MyList := TList.Create;
...
MyList.Free;
MyList := TList.Create;
...
MyList.Free;

в данном случае значение указателя MyList изменяется 2 раза при присвоении MyList := TList.Create;, при этом второй раз значение указателя заменяется на новое. 

Знаменитый Access Violation (AV | EAccessViolation)
После второго вызова  MyList.Free; указатель MyList не изменяется и указывает на "освобожденный" блок памяти. Отсюда растут ноги знаменитого Access Violation (AV | EAccessViolation). Если после MyList.Free; обратиться к полю, свойству или методу класса TList через указатель MyList, например c := MyList.Count;, мы получим эту распространенную ошибку.

Free vs FreeAndNil или освобождение объектов
Итак мы разобрались с переменными-указателями на объекты. Давайте рассмотрим освобождение объектов. Для освобождения в Delphi предусмотрено 2 стандартных механизма:
  1. метод Free класса TObject
  2. процедура FreeAndNil из модуля SysUtils
Рассмотрим метод Free:
procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

Все что делает данные метод - вызывает стандартный деструктор Destroy при условии что указатель на объект не равен nil
отсюда получаем ошибку AV при попытке повторного освобождения объекта если писать так, например:
MyList := TList.Create;
...
MyList.Free;//указатель на объект не очищается

MyList.Count;//AV - пытались вызвать метод освобожденного объекта

Рассмотрим процедуру FreeAndNil:
procedure FreeAndNil(var Obj);
var
  Temp: TObject;
begin
  Temp := TObject(Obj);//сохраняем временный указатель на объект
  Pointer(Obj) := nil;//очищаем указатель
  Temp.Free;//освобождаем объект
end;
Наш пример с "двойным" освобождением завершится без ошибок
MyList := TList.Create;

...
FreeAndNil(MyList);//указатель на объект очищается

FreeAndNil(MyList);//срабатывает проверка на self<>nil в TObject.Free; в procedure FreeAndNil(var Obj);
Машинный код для TObject.Free составляет 12 байт это 6 строк ассемблерного псевдокода
для FreeAndNil - 35 байт или 16 строк ассемблерного псевдокода (из них 5 байт или одна строчка кода  - вызов той же TObject.Free) оверхэд составляет 30 байт =) или 10 строк
Что дает оверхэд - очистка указателя при чем до вызова TObject.Free.



Комментариев нет:

Отправить комментарий