Delphi 接口使用中,对象生命周期管理,如何释放需要注意的问题。pcplayer 原创!
网上有篇文章《Delphi接口编程的两大陷阱》,里面提到接口的生存期管理的问题。但该文章里面提到的两个问题,其实都是对
Delphi 不理解导致的。
先说该篇文章中提到的第一个问题为什么是该文章作者不理解 DELPHI
导致他认为那是不可理解的陷阱。然后俺再来重点解释接口的生命周期管理。
一. 接口 - 对象。
假设有接口定义:
IMyTask = interface
end;
然后有个类实现了该接口:
TMyClass = class(TComponent, IMyTask)
public
end;
然后有个该类的对象实例:MyObj :=
TMyClass.Create(Application);
这时候,按 DELPHI 的语法规则,当然可以这样做:
MyIntf := MyObj as IMyTask;
也可以不用 AS
其实还有很多其它写法。总之,这里是从 MyObj 对象实例,取得它实现的接口的指针!而不是做类型转换!
前面提到的那篇文章的作者却以为这样做是【类型转换】,因此他就试图通过类型转换来做:MyObj := MyIntf...这样做当然是不行的!因为他的理解错误,使得他认为那是个陷阱。
事实上,一个对象可以实现多个接口。比如 TComponent
肯定也实现了IInterfaceComponentRefer
AInft:
AIntf := MyObj
as
注意到没,一个对象可以拥有多个接口,因此,获取的该对象的接口的指针,肯定不是指向该对象的。否则两个接口的指针就打架了!所以,这里不能直接做类型转换,把指针转为对象!
那么,当我们有一个接口,如何获得它所在的对象呢?TComponent
实现的
因此,如果是继承自 TComponent
的对象,它肯定有实现
AObj : TComponent;
AObj :=
------------------------------------------------------------
二. 接口生存周期的管理:
在 Delphi 里面使用接口,一个实现某个接口的类,必须实现 IInterface 接口的三个方法。
IInterface = interface
上述3个方法里面,_Release 方法就负责释放对象自己。
当然,我们自己写一个类的时候,不用自己去实现这三个方法,只要我们的类从 TInterfacedObject 继承。TInterfacedObject 类已经实现了上述三个方法。
继 承自
但是.......但是.......但是来了,需要注意的事情来了!
如果,你的类,是从 TComponent 类继承下来的,而且实现了你自己写的某个接口..........
TComponent 类的对象实例的接口引用为 0 的时候,并不会释放对象实例自己!你必须自己去释放对象本身,调用对象的 Free 方法。
这还是比较简单的概念。麻烦的是,如果你的对象A,拥有另外一个对象B的接口引用,当对象A被释放的时候,A内部的接口引用自然变成 nil,则会导致对象B内部的引用计数减一。问题是,如果在这之前,对象B已经被 FREE 了,这时候就会出现 AV 错误。
因此,这时候,一定要注意释放顺序!
当你给某个 TForm 的子类比如 TForm2 增加一个你自己定义的接口的时候,这样的错误就容易出现了。
以下是俺对该使用场景做的总结,此总结经过俺自己写代码测试确认过:
1. 如果该 Form 是属于 Application 的,则程序退出时,该 FORM 比主FORM先被消灭;
再次重复一下上面的说法:
因此,程序退出时,要在主 FORM 里面的 OnClose 里面主动释放接口。
总 结:一定要注意释放顺序。而释放顺序要遵循的原理,则是 TComponent
的子类,释放完接口,对象不会自动释放,必须主动释放对象;但对象被释放以后再释放引用它的接口(比如 MyIntf :=
nil;