不应该在对象引用设置为nil时调用Free每次调用时都会抛出一个访问冲突吗?

时间:2011-12-21 14:19:26

标签: delphi delphi-xe

我从单位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;

但是我在DBXCommon.pas中看到了更像这样的构造(首先分配Nil,然后是免费)。这是一些我不知道的构造,或者这是否真的导致访问冲突每次这段代码被调用?

3 个答案:

答案 0 :(得分:15)

在空引用上调用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;

这具有相同的效果。

答案 1 :(得分:6)

可以安全地在Free引用上调用nil,因为它在调用Self <> nil之前对Destroy进行了实施检查。请参阅Embarcadero forum中的Allen Bauer解释为什么引入TObject.Free。我在这里只包括相关的引用:

  

在TObject上引入非虚拟Free方法的唯一原因是在析构函数中使用它作为简单的简写:

if FField <> nil then
  FField.Destroy;

答案 2 :(得分:4)

TObject.Free基本上实现为if Self <> nil then Destroy,因此上面的代码不应引发任何异常。