Delphi: почему FreeAndNil * на самом деле * не обнуляет мой объект? - PullRequest
5 голосов
/ 07 ноября 2011

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

program Project6;
{$APPTYPE CONSOLE}
uses
  SysUtils;
type
  TMyObject = class(TObject)
  public
    FField1:  string;
    FField2:  string;
  end;
  TBigObject = class(TObject)
  public
    FMyObject:  TMyObject;
    procedure Bind(var MyObject:  TMyObject);
    procedure Free();
  end;
procedure TBigObject.Bind(var MyObject: TMyObject);
begin
  FMyObject := MyObject;
end;
procedure TBigObject.Free;
begin
  FreeAndNil(FMyObject);
  Destroy();
end;
var
  MyObject:   TMyObject;
  BigObject:  TBigObject;
begin
  try
    MyObject := TMyObject.Create();
    BigObject := TBigObject.Create();
    BigObject.Bind(MyObject);
    BigObject.Free();
    if (Assigned(MyObject)) then begin
      WriteLn('Set MyObject free!');
      MyObject.Free();
    end;
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

(Не берите в голову ужасный дизайн.) Теперь я не понимаю, почему FreeAndNil действительно освобождает MyObject, но Assigned(MyObject) оценивается как true (давая AV на MyObject.Free()).

Может ли кто-нибудь помочь мне просветить меня?

Ответы [ 4 ]

15 голосов
/ 07 ноября 2011

MyObject - это переменная, отличная от поля FMyObject. И вы только nil заполняете поле FMyObject.

FreeAndNil освобождает объект, на который указывает, и nil s переменную, которую вы передали. Она не обнаруживает магическим образом и nil все другие переменные, которые указывают на объект, который вы освободили.

FreeAndNil(FMyObject); делает то же самое, что и:

object(FMyObject).Free();
FMyObject=nil;

(Технически это не совсем правильно, приведение к объекту является переосмысленным приведением из-за нетипизированного параметра var, но здесь это не имеет значения)

И это, очевидно, только изменяет FMyObject, а не MyObject


О, я только что заметил, что вы скрываете оригинальный метод Free? Это безумие. FreeAndNil все еще использует оригинал Free. Это не поразило вас в вашем примере, потому что вы вызываете Free для переменной со статическим типом TBigObject, а не FreeAndNil. Но это квитанция за катастрофу.

Вместо этого вы должны переопределить деструктор Destroy.

14 голосов
/ 07 ноября 2011

Причина проста: вы указываете одну ссылку, а другую нет.Рассмотрим этот пример:

var
  Obj1, Obj2: TObject;
begin
  Obj1 := TObject.Create;
  Obj2 := Obj1;
  FreeAndNil(Obj1);
  // Obj1 is released and nil, Obj2 is non-nil but now points to undefined memory
  // ie. accessing it will cause access violations
end;
8 голосов
/ 07 ноября 2011

У вас есть две копии ссылки на объект, но вы устанавливаете только одну из них на ноль.Ваш код эквивалентен этому:

i := 1;
j := i;
i := 0;
Writeln(j);//outputs 1

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

Приведение примера в терминах ссылок на объекты выглядит так:

obj1 := TObject.Create;
obj2 := obj1;
obj1.Free;//these two lines are
obj1 := nil;//equivalent to FreeAndNil
//but obj2 still refers to the destroyed object

В сторону: Вы никогда не должны вызывать Destroy напрямую и никогда не объявлять метод с именем Free.Вместо этого переопределите Destroy и вызовите статический Free, определенный в TObject, или действительно FreeAndNil.

1 голос
/ 07 ноября 2011

В вашем коде есть несколько особенностей.

Во-первых, вы не должны переписывать Free, вы должны переопределить виртуальный деструктор (Destroy) вашего класса.

Но ISTM, что BigObject не является владельцем MyObject, поэтому BigObject вообще не должен пытаться освободить его.

Как уже сказал CodeInChaos, FreeAndNil освобождает только одну переменнуюв этом случае поле FMyObject.В любом случае FreeAndNil не требуется, поскольку после освобождения объекта ничего не может произойти.

Назначенный элемент нельзя использовать для проверки, был ли объект уже освобожден.Он может проверять только nil, а FreeAndNil устанавливает одну ссылку на nil, а не сам объект (это невозможно).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...