Почему объекты Delphi назначаются даже после вызова .Free? - PullRequest
24 голосов
/ 24 октября 2008

Почему в Delphi функция Assigned () по-прежнему возвращает True после вызова деструктора?

В приведенном ниже примере кода на консоли будет написано «sl все еще назначен».

Однако я могу вызвать FreeAndNil (sl); и он не будет назначен.

Я некоторое время программировал на Delphi, но для меня это никогда не имело смысла.

Может кто-нибудь объяснить?

program Project1;
{$APPTYPE CONSOLE}
uses SysUtils, Classes;

var
  sl : TStringList;

begin
  sl := TStringList.Create;
  sl.Free;
  if Assigned(sl) then
    WriteLn('sl is still assigned')
  else
    WriteLn('sl is not assigned');
end.

Я попытался сравнить операции VCL ... FreeAndNil короток и приятен и имеет смысл:

procedure FreeAndNil(var Obj);
var
  P: TObject;
begin
  P := TObject(Obj);
  TObject(Obj) := nil;  // clear the reference before destroying the object
  P.Free;
end;

Но TObject.Free находится в таинственном ассемблере, которого я не понимаю:

procedure TObject.Free;
asm
        TEST    EAX,EAX
        JE      @@exit
        MOV     ECX,[EAX]
        MOV     DL,1
        CALL    dword ptr [ECX].vmtDestroy
@@exit:
end;

Ответы [ 4 ]

37 голосов
/ 24 октября 2008

Если вы используете sl.Free, объект освобождается, но переменная sl все еще указывает на недопустимую память.

Используйте FreeAndNil (sl) для освобождения объекта и очистки указателя.

Кстати, если вы делаете:

var
  sl1, sl2: TStringList;
begin
  sl1 := TStringList.Create;
  sl2 := sl1;
  FreeAndNil(sl1);
  // sl2 is still assigned and must be cleared separately (not with FreeAndNil because it points to the already freed object.)
end;




procedure TObject.Free;
asm
    TEST    EAX,EAX
    JE      @@exit              // Jump to exit if pointer is nil.
    MOV     ECX,[EAX]           
    MOV     DL,1
    CALL    dword ptr [ECX].vmtDestroy  // Call cleanup code (and destructor).
@@exit:
end;
14 голосов
/ 24 октября 2008

Delphi VCL 'объекты' на самом деле всегда являются указателями на объекты, но этот аспект обычно скрыт от вас. Простое освобождение объекта оставляет указатель повисшим, поэтому вместо этого следует использовать FreeAndNil.

«Таинственный Ассемблер» переводится примерно на:

if Obj != NIL then
  vmtDestroy(obj);  // which is basically the destructor/deallocator.

Поскольку Free сначала проверяет NIL, безопасно вызывать FreeAndNil несколько раз ...

3 голосов
/ 08 сентября 2012

Свободный метод TObject похож на «оператор удаления» в C ++. Вызов free сначала вызовет функцию Destroy, а затем освободит блок памяти, выделенный для объекта. По умолчанию указатель на память не устанавливается на ноль, поскольку при этом будет использоваться одна инструкция.

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

Например. В функции, где объект создается и затем освобождается в конце функции, нет смысла устанавливать переменную в ноль, поскольку это просто тратит время процессора.

Но для глобального объекта или поля, на которое можно будет ссылаться снова позже, вы должны установить его на ноль. Используйте FreeAndNil или просто установите указатель на ноль самостоятельно, не имеет значения. Но держитесь подальше от обнуления переменных, которые не должны обнуляться по умолчанию.

1 голос
/ 25 сентября 2014

У нас есть простые правила:

  1. Если вы хотите использовать Assigned(), чтобы проверить, уже создан объект Obj или нет, затем убедитесь, что вы используете FreeAndNil(Obj), чтобы освободить его.

  2. Assigned() говорит только, назначен адрес или нет.

  3. Ссылке на локальный объект всегда присваивается адрес мусора (некоторый случайный адрес), поэтому перед использованием его рекомендуется установить в ноль.

Пример: (Это не полный код)

{Opened a new VCL application, placed a Button1, Memo1 on the form
Next added a public reference GlobalButton of type TButton
Next in OnClick handler of Button1 added a variable LocalButton 
Next in body, check if GlobalButton and LocalButton are assigned}

  TForm2 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    GlobalButton: TButton;
  end;

procedure TForm2.Button1Click(Sender: TObject);
var
  LocalButton: TButton;
begin
  if Assigned(GlobalButton) then  
    Memo1.Lines.Add('GlobalButton assigned');
  if Assigned(LocalButton) then  
    Memo1.Lines.Add('LocalButton assigned');
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...