我已经阅读了反对FreeAndNil的理由,但仍然不明白为什么我不能在一个类的析构函数中使用这种方法?有人能解释一下吗?
更新:我认为来自Eric Grange的评论对我最有帮助。链接表明这并不明显如何处理它,主要是品味问题。另外,FreeAndInvalidate方法也很有用。
我已经阅读了反对FreeAndNil的理由,但仍然不明白为什么我不能在一个类的析构函数中使用这种方法?有人能解释一下吗?
更新:我认为来自Eric Grange的评论对我最有帮助。链接表明这并不明显如何处理它,主要是品味问题。另外,FreeAndInvalidate方法也很有用。
SomeObject.Free; // Destructor called
if Assigned(SomeObject.PropertyA) then // SomeObject is destroyed, but still used
SomeObject.PropertyA.Method1;
编辑
另一方面,有人可能会认为如果不使用FreeAndNil,代码也不会崩溃。因为即使对象已被销毁,内存可能尚未被重用并且所有结构可能仍然完整。如果使用Free而不是FreeAndNil来销毁PropertyA,则上面的代码甚至可能运行无误。
如果使用FreeAndNil来销毁SomeObject,无论解构函数中的代码是什么,您也将看到真正的问题。
因此,尽管我同意它可能会隐藏真正的设计缺陷,并且个人在解构函数中不使用FreeAndNil,但它不是发现这种设计缺陷的某种神奇解决方案。
procedure Test;
var
LObj: TObject;
begin
LObj := TObject.Create;
try
{...Do work...}
finally
//The LObj variable is going out of scope here,
// so don't bother nilling it. No other code can access LObj.
//FreeAndNil(LObj);
LObj.Free;
end;
end;
LObj
变量设为nil
是毫无意义的,原因已经给出。但是,如果一个对象变量在应用程序生命周期内可以被实例化和释放多次,那么检查对象是否确实被实例化是必要的。检查这一点的简单方法是检查对象引用是否已设置为nil
。为了方便设置nil
,FreeAndNil()
方法将同时释放资源并为您设置nil
。然后,在代码中,您可以使用LObj = nil
或Assigned(LObj)
来检查对象是否被实例化。.Free
或FreeAndNil()
的情况是一个灰色的领域,但大多数情况下,.Free
应该是安全的,而在解构函数中将子对象的引用设为nil是不必要的。关于如何处理构造函数和解构函数中的异常有各种不同的观点。.Free
或FreeAndNil()
,那么没问题,但请注意,由于未将释放的对象引用设为nil而随后访问它可能会导致错误的代价非常高。如果随后访问指针(对象已被释放但引用未设为nil),那么您可能会不幸地发现,内存损坏的检测发生在访问已释放但未设置为nil的对象引用的许多代码行之后。这种错误可能需要很长时间才能修复,是的,我知道如何使用FastMM。FreeAndNil
(不知道为什么),但现在不再使用了。让我停止使用它的原因与变量是否需要在之后设置nil
无关。它与代码更改有关,特别是变量类型的更改。TSomething
更改为接口类型ISomething
后被咬伤过。 FreeAndNil
不会抱怨,并愉快地继续在接口变量上执行其工作。这有时会导致神秘的崩溃,无法立即追溯到其发生的地方,需要花费一些时间才能找到。Free
。而且当我认为有必要的时候,会显式地将变量设置为nil
。FreeAndNil
和FreeAndInvalidate
,并提到微软安全开发生命周期现在建议使用类似于FreeAndInvalidate
的东西:链接。procedure FreeAndInvalidate(var obj);
var
temp : TObject;
const
INVALID_ADDRESS = $8123; //address on same page as zero address (nil)
begin
//Note: Code is public domain. No attribution required.
temp := TObject(obj);
Pointer(obj) := Pointer(INVALID_ADDRESS);
temp.Free;
end;