我的问题是,如何检查对象是否已被销毁?除了 Assigned() 之外还有什么可以检查对象是否仍然存在?
program Project1;
uses System.SysUtils;
type TObj = class
public
Name: string;
end;
var AnObj, AnObj2 : TObj;
begin
try
try
AnObj := TObj.Create;
AnObj.Name := 'Testing';
AnObj2 := AnObj; // AnObj passed to other procedures as param
FreeAndNil(AnObj2); // somewhere else "Free" the object out of my control
// as a result, AnObj is still assigned but the object is destroyed
finally
if Assigned(AnObj) then // AnObj is assigned, HOW COULD I IMPROVE HERE?
FreeAndNil(AnObj); // Exception: Invalid pointer operation
end;
except
on E:Exception do
writeln(E.Message);
end;
readln;
end.
Run Code Online (Sandbox Code Playgroud)
如何检查一个对象是否已经被销毁?
简短的回答是你不能。没有任何机制可以让您在手动内存管理下检查对象的有效性。
如果您想跟踪有效性以进行调试,您可以使用自定义内存管理器(如完全调试模式下的 FastMM),它可以跟踪所有引用并在您访问悬空指针时进行报告。
FreeAndNil与Assigned仅在您对对象有单一引用时才有效。如果您有更多的它分崩离析,因为FreeAndNil可以nil只参考你叫它时。所有其他引用将成为悬空指针并且Assigned无法检测悬空指针。
简而言之,Assigned仅当引用指向有效的对象实例或包含nil值时才有效。在手动内存管理下,您必须跟踪并确保您的引用始终包含有效值,如果您需要使用Assigned.
使用Assigned时,你保持单身的对象引用
如果您有对对象的单一引用,您可以使用Assigned延迟初始化模式,如果尚未创建对象实例,您将在其中创建对象实例。但是您必须首先确保对象引用包含nil引用。
nil只有当对象是类字段、对象实例字段或全局变量时,对象引用才会被自动初始化。
如果您不打算在释放后重用该对象引用,则可以使用它Free来释放其内存。
如果你有可重用的引用,你想多次创建和释放对象实例,你必须使用FreeAndNil,或者在调用后将引用设置为 nil Free。这是您可以确保Assigned在对象释放后工作的唯一方法。同样,此Assigned/FreeAndNil模式仅在您保留对该对象的一个且仅一个引用时才有效。
使用Assigned时,你保持多个对象引用
如果可能,永远不要保留对一个对象实例的多个引用。如果您确实必须保留对对象实例的多个引用,则需要实现某种机制以确保您可以通知所有引用该对象不再有效。
有几种方法可以做到这一点:
TComponent作为基类-在这里你可以使用它的通知系统以获得当对象实例将被销毁的通知注意:对于 2. 和 3.,您必须nil在收到通知时手动对对象的所有引用进行销毁。如果您不这样做,您未设置的任何引用nil都将无效,任何使用的悬空引用都Assigned将无法正常工作。
如果您遇到将对象作为参数传递的情况,并且Free在该对象上调用了一些超出您控制的代码,那么您正在处理所有权转移。您创建了对象,但随后将所有权(释放对象的责任)转移给了其他一些代码。之后您不应再次使用该对象引用(除非在某些狭窄的情况下,当您确定传输完成后的立即代码不会触发对象释放时)。
只有当有一些通知机制(如前所述)可以在对象实例被释放时通知您时,您才可以使用您不拥有的对象引用(在您转移所有权之后)。
此外,您永远不应该免费使用您不拥有的对象。在你的例子中,即使你能得到通知,对象将被销毁,你所能做的就是nil指向它的引用。你不应该试图释放它。