有没有办法确保我们持有对一个对象的可用引用,即确保它尚未被释放,使非零引用悬空。
7 回答
如果您使用 FastMM4 作为内存管理器,则可以检查该类是否不是TFreeObject。或者,在更标准的情况下,使用一个例程,通过检查类 VMT
来验证您的对象是它所说的那样。
这样的 ValidateObj 函数已经存在了一段时间(Ray Lischner 和 Hallvard Vassbotn:http ://hallvards.blogspot.com/2004/06/hack-6checking-for-valid-object.html )
这是另一个:
function ValidateObj(Obj: TObject): Pointer;
// see { Virtual method table entries } in System.pas
begin
Result := Obj;
if Assigned(Result) then
try
if Pointer(PPointer(Obj)^) <> Pointer(Pointer(Cardinal(PPointer(Obj)^) + Cardinal(vmtSelfPtr))^) then
// object not valid anymore
Result := nil;
except
Result := nil;
end;
end;
更新:有点谨慎......上述函数将确保结果为 nil 或有效的非 nil 对象。如果内存管理器已经重新分配了先前释放的内存,它不能保证 Obj 仍然是您认为的那样。
不。除非您使用引用计数或垃圾收集器之类的东西来确保在它们具有零引用之前不会释放任何对象。
如果您使用接口,Delphi 可以为您进行引用计数。当然 Delphi for .Net 有一个垃圾收集器。
如前所述,您可以使用 Delphi 或内存管理器内部的知识来检查有效的指针或对象,但它们并不是唯一可以为您提供指针的。因此,即使使用这些方法,您也无法涵盖所有指针。而且你的指针也有可能再次有效,但给了其他人。所以它不是你要找的指针。你的设计不应该依赖它们。使用工具来检测您所做的任何参考错误。
标准,不...
这就是为什么 VCL 组件可以注册自己以收到有关对象销毁的通知,以便它们可以从内部组件列表中删除引用或只是重置它们的属性。
因此,如果您想确保没有任何无效的引用,他们有两个选择:
- 实现每个类都可以订阅的销毁通知处理程序。
- 以引用不通过不同对象传播的方式修复您的代码。例如,您只能通过另一个对象的属性提供对引用的访问。而不是复制对私有字段的引用,而是访问另一个对象的属性。
正如其他人所说,没有明确的方法,但是如果您管理得当,那么 FreeAndNil 例程将确保您的变量在不指向任何内容的情况下为零。
无论如何,检查参考是否有效通常不是一个好主意。如果引用无效,您的程序将在使用无效引用的地方崩溃。否则,无效的引用可能会存活更长时间,调试变得更加困难。
以下是一些关于为什么最好在无效引用上崩溃的参考。(他们谈论 Win32 中的指针,但这些想法仍然相关):
不幸的是,没有办法 100% 保证指向任何东西的指针仍然有效,除非精心编写正确的代码。
使用接口引用(而不是对象引用)可以避免这些无效指针问题,因为您的代码中不再显式调用 Free。