Почему объекты Delphi назначаются даже после вызова. Свободно?
В 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;
Ответы
Ответ 1
Если вы используете 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;
Ответ 2
Delphi VCL "объекты" на самом деле всегда указывают на объекты, но этот аспект обычно скрыт от вас. Просто освобождение объекта оставляет указатель, висящий вокруг, поэтому вместо него следует использовать FreeAndNil.
"Таинственный ассемблер" грубо переводит:
if Obj != NIL then
vmtDestroy(obj); // which is basically the destructor/deallocator.
Поскольку Free проверяет сначала NIL, он безопасно вызывать FreeAndNil несколько раз...
Ответ 3
Свободный метод TObject подобен "оператору удаления" в С++. Вызов бесплатно сначала вызовет функцию Destroy, а затем освободит блок памяти, который был выделен для объекта. По умолчанию указатель на память не устанавливается на ноль, потому что это будет использовать одну команду.
В большинстве случаев правильная вещь - не устанавливать указатель на ноль, потому что в большинстве случаев это не имеет значения. Однако иногда это важно, поэтому для этих случаев нужно указывать только указатель.
Например. В функции, где объект создается и затем освобождается в конце функции, нет смысла устанавливать переменную в ноль, поскольку это просто тратит время процессора.
Но для глобального объекта или поля, на которые можно ссылаться позже, вы должны установить его на ноль. Используйте FreeAndNil или просто установите указатель на нуль самостоятельно, не имеет значения. Но держитесь подальше от нулевых переменных, которые по умолчанию не нужно обнулять.
Ответ 4
У нас есть простые правила:
-
Если вы хотите использовать Assigned()
, чтобы проверить, создан ли объект Obj
или нет,
затем убедитесь, что вы используете FreeAndNil(Obj)
, чтобы освободить его.
-
Assigned()
указывает только, назначен ли адрес или нет.
-
Ссылка на локальный объект всегда присваивается адресу мусора (некоторый случайный адрес), поэтому полезно использовать его перед использованием.
Пример: (Это не полный код)
{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;