Не должен ли вызов Free для ссылки на объект, установленной на nil, вызывать нарушение прав доступа при каждом его вызове? - PullRequest
7 голосов
/ 21 декабря 2011

Я получаю нарушения прав доступа с устройства DBXCommon.pas (в Delphi XE). Когда я смотрю на код, я вижу такие вещи (восклицательные знаки):

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext;
  const ConnectionProperties: TDBXProperties): TDBXConnection;
var
  ConnectionBuilder:  TDBXConnectionBuilder;
  DelegatePath:       TDBXDelegateItem;
  Connection:         TDBXConnection;
  CombinedProperties: TDBXProperties;
begin
  //...
  ConnectionBuilder := TDBXConnectionBuilder.Create;
  Connection        := nil;
  try
    //..lots of setting ConnectionBuilder properties
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password];
    Connection := ConnectionBuilder.CreateConnection;
    Connection.Open;
    Result     := Connection;
!!  Connection := nil;
  finally
!!  Connection.Free;
    ConnectionBuilder.Free;
  end;
end;

Но я вижу такие конструкции (сначала присваиваем Nil, затем Free) гораздо больше в DBXCommon.pas. Это какая-то конструкция, которую я не знаю, или это действительно вызывает нарушение прав доступа каждый раз, когда этот фрагмент кода вызывается?

Ответы [ 3 ]

15 голосов
/ 21 декабря 2011

Вызов Free по нулевой ссылке всегда безопасен. Посмотрите в реализации TObject.Free, чтобы понять, почему.

Этот код является примером заводской функции . Его задача заключается в создании нового экземпляра класса, но в случае его сбоя необходимо убедиться, что он не пропускает наполовину созданный экземпляр, когда выдает исключение, поэтому он вызывает Free. Когда он уверен, что получится, он передает право собственности на результат вызывающей стороне. Он по-прежнему вызывает Free, но если это уже переданное право собственности, то в итоге он вызывает Free по нулевой ссылке, и никакого вреда не будет. Этот код является то, что передает право собственности:

Result := Connection;
Connection := nil;

То, как I напишет фабричную функцию, избавит от отдельной переменной Connection. Я бы построил результат непосредственно в Result, но освободил бы его, если бы было исключение, например:

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext;
  const ConnectionProperties: TDBXProperties): TDBXConnection;
var
  ConnectionBuilder:  TDBXConnectionBuilder;
  DelegatePath:       TDBXDelegateItem;
  Connection:         TDBXConnection;
  CombinedProperties: TDBXProperties;
begin
  //...
  ConnectionBuilder := TDBXConnectionBuilder.Create;
  try
    //..lots of setting ConnectionBuilder properties
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password];
    Result := ConnectionBuilder.CreateConnection;
    try
      Result.Open;
    except
      Result.Free;
      raise;
    end;
  finally
    ConnectionBuilder.Free;
  end;
end;

Это имеет тот же эффект.

6 голосов
/ 21 декабря 2011

Безопасно вызывать Free по ссылке nil, так как реализация проверяет Self <> nil перед вызовом Destroy.См. Объяснение Аллена Бауэра на форуме Embarcadero почему был введен TObject.Free.Я включил только соответствующую цитату здесь:

Единственная причина для введения не виртуального метода Free на TObject, была для использования в деструкторах в качестве простого сокращения для:

if FField <> nil then
  FField.Destroy;
4 голосов
/ 21 декабря 2011

TObject.Free в основном реализовано как if Self <> nil then Destroy, поэтому приведенный выше код не должен вызывать никаких исключений.

...