关于接口 Interface 在使用时的一点问题,讨论(100分)

  • 主题发起人 主题发起人 QSmile
  • 开始时间 开始时间
Q

QSmile

Unregistered / Unconfirmed
GUEST, unregistred user!
关于接口 Interface 在使用时的一点问题,讨论

比如我定义一个接口.
ITest = interface
procedure Test;
end;

用一个对象实现它
TTest = class(TInterfacedObject, ITest)
procedure Test;
end;

使用时我这样用
var
test:ITest;

test := ITest(TTest.Create);
test.test;


这样是没有问题的,
而这时再用
test := nil

时 test 对象就被 Free 了.
或再用
test := ITest(TTest.Create);
前一次建立的 test 对象也是被 Free 了的.

而这时我想把 test 的指针清空或改变, 而又不想它指向的对象被 Free.
我应该如何做呢?
因为我们建一些象方放 Tlist 里, 建立后如这样
for i := 0 to 100 do
begin
test := ITest(TTest.Create);
test.test;
test.xxx :=1000;
m_list.add(Pointer(test))
// m_list 为 TList
end;

这样的话,只有最后的个接口是有效的, 前面的都被自动 Free 了.

有会么办法呢
 
接口引用计数的,不用接口就好了。
 
我也知道不用接口就好了.
这样如何做呢? 手动加一?
 
TList改为TInterfaceList即可
 
nicai_wgl,
真是一语惊醒梦中人, 手动增加减少记数就可以了.
 
接口只是服务的声明,而真正的服务由对象来提供。
按理说接口不能控制对象的声明周期
但是在Delphi中,基本上为了方便实现接口的类都是从TInterfacedObject继承
在TInterfacedObject中实现了接口IInterface的_Release方法,具体代码如下:
function TInterfacedObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
对象被删除是因为
if Result = 0 then
Destroy;
所以你可以自己写一个类,继承自TObject
比如TMyInterfaceObject = class
其中所有的方法和属性都和TInterfacedObject相同,只是在实现_Release方法的时候代码改为如下形式:
function TMyInterfaceObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
end;
而你自己的那个类继承自这个类,如下
TTest = class(TMyInterfaceObject, ITest)
procedure Test;
end;
这样对象的生命周期就由你自己决定而不是交给编译器了
 
最好不要手动增加或者减少计数
这样容易难以理解,并有可能因为疏忽造成引用内存出错或者内存溢出
 
最好不要手动增加或者减少计数
这样容易难以理解,并有可能因为疏忽造成引用内存出错或者内存溢出
----------------------------------
那你有更好的方法吗?

用 TInterfaceList 应该可以.
 
我不是已经提供了方法了吗?
难得我打字半天你居然没有发现:)
 
你的解决方法与我手动增加减少记数好象没什么区别
 
问题是,你这样处理以后的接口,如何 Free 呢?

我是在 DLL 里用的. 主程序调 DLL 的一个函数, DLL 返回一个接口.

你这样做的话, 接口用完后,对象不能 Free 吧. (因为记数为 0 你也没有 Free)
 

pointer(test) := nil
就可以了
 
按照我的理解,接口只是一种服务的集合,不应该控制提供服务的对象的生命周期
所以使用我的方法将控制对象生命周期的控制权完全交给我们
这样使用接口就和我们以前使用对象没有区别了
既然你能自己Create,就负责在不需要的时候Free
不要把这些事交给接口来做
有的时候编译器太智能了未必是一件好事
 
to QSmile:
for i := 0 to 100 do
begin
test := ITest(TTest.Create);
test.test;
test.xxx :=1000;
m_list.add(Pointer(test))
// m_list 为 TList,此处没有增加引用计数
//再下一循环中,test 被设为新接口时,旧接口引用计数为零,被释放,list中的指针
//失效。改为IInterface(m_list[mlist.add(nil)]) := test as IInterface;即可增加
//引用计数;
//释放时,对List中的每项进行IInterface(m_list):=nil;即可进行释放。
//可以参照TInterfaceList的源码,没必要修改release方法。
end;
 
不要将简单问题复杂化。
你既然让接口参与了你的设计,那么就不要将接口和对象的生命期混合。
如果你需要将对象和接口的生命期分开,那么就重载_release和_AddRef方法,
Result := 1即可。那样,不管对象转换为接口多少次,接口的引用次数永远都是1,最后手工释放。
 
to muhx,
你说的都正确,但我已经说了,我是要在 DLL 中使用接口. 这是我的前提.


to : 铁盒子
你的方法是可行的.也与我的方法在本质上是一致的.
我是在加入 List 时, 手动调用 _AddRef . 在释放是再手动调用 _Release


----------------------------------
总结, 也许在我这个特例下,用 TInterfaceList 是个好主意, TInterfaceList 是靠把它强行保存成接口对接口的赋值,以让系统来调用(自动) _AddRef 的.
 
多人接受答案了。
 
后退
顶部