转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 软件开发 >> Delphi程序 >> 正文
非COM环境下的接口编程--问题,技巧,应用(一)         ★★★★

非COM环境下的接口编程--问题,技巧,应用(一)

作者:闵涛 文章来源:闵涛的学习笔记 点击数:1957 更新时间:2009/4/23 18:35:03
添加一个用于释放所管理对象的方法:

procedure TFooManager.DelAFoo(id:integer);

var

 i:integer;

begin

 if FooNum>0 then

 begin

   FList[id].Free;

   for i:=id to FooNum-2 do

   begin

    FList[i]:=FList[i+1]; //移动剩余元素,使对象在flist中保持连续存贮。

   end;

   FList[FooNum-1]:=nil;

   Dec(FooNum);

 end;

end;

这个方法根据传入的一个ID值释放指定的对象(在管理多种不同的对象时,还需要接收一个代表所需要释放对象类型的参数以决定释放哪种类型的对象)。好,一切看上去都很正常,但当我们从DLL外在调用工厂方法创建对象的时候问题出现了:

procedure CallCreateFoo;

var

 tempfoo:IFoo;//要创建的Foo对象的接口IFoo

begin

 tempfoo:=FooMan.CreateAFoo; //FooMan是工厂类的对象的接口

end;

当我们需要在这个过程外使用刚才创建的对象时(例如我们可以使用工厂类的一个方法TFooManager.GetFooByID(id: integer)根据传入的id找到flist中指定的对象,如刚才我们通过工厂方法创建的那对象),会出现一个内存访问错误,仔细观察后会发现我们刚才创建的对象根本不在内存中!为什么?由于delphi中的接口都继承自Iinterface,这是一个与Iunknown一样的接口,这意味着我们的对象都必须实现Iunknown中的那3个方法,更进一步的我们为了不用手工书写这些代码我们的类都继承自默认实现了Iinterface的类TinterfacedObject。然而错误的根源就在这个地方,继承自TinterfacedObject的对象根本不允许我们手工管理它的生命周期,因为Iunknown的实现类会根据对象中的引用计数来维护对象的生命周期,而上面的tempfoo是一个过程的局部变量,当它离开作用域时会被delphi编译器自动调用tempfoo._ Release(这是Iunknown的方法),这个方法在引用计数为0的时候将自动释放对象!而我们在调用TFooManager.CreateAFoo方法时其中仅做了一次as操作result:=FList[FooNum-1] as IFoo;(delphi会在这时自动增加一个对象的引用计数),所以引用计数为1在_ Release后变为0,于是在我们想访问我们创建的对象之前这个对象就已经不存在了。好了,问题的起因弄的很清楚了,要解决也不难,我们只用在返回请求接口之前进行一次_AddRef操作增加引用计数值,这样除非我们手工释放对象,否则引用计数都不会为0,如下的改进:

function TFooManager.CreateAFoo: IFoo;

begin

 …

 FList[FooNum-1]:=TFoo.Create;

 (FList[FooNum-1] as IUnKnown)._AddRef;

 result:=FList[FooNum-1] as IFoo;

end;

好了,似乎我们已经克服了所有的困难,是这样吗?不是,麻烦马上又出现了!当我们这个时候调用工厂类的DelAFoo方法时会抛出更多的异常!。当我们释放对象的时候会调用到TinterfaceObject的free方法,在调用这个方法前编译器会自动调用TInterfacedObject.BeforeDestruction方法(事实上这是一个从Tobject继承下来的方法,但在Tobject中它没有任何的实现),这个方法代码如下:

procedure TInterfacedObject.BeforeDestruction;

begin

  if RefCount <> 0 then

    Error(reInvalidPtr);

end;

看到这里问题明白了吧,由于我们手动增加了引用计数值,使那个值在释放对象前也不会为0,而上面的代码在引用计数值为0的时候会抛出一个异常。解决这个问题的办法很简单,我们只需要再自己定义一个我们的TinterfacedObject类TourInterfacedObject,在这个类中复写(override,因为这是一个虚函数)BeforeDestruction,让它的代码部分空白:

procedure TInterfacedObject.BeforeDestruction;

begin

end;

然后我们DLL中所有的类只用从TourInterfacedObject继承就可以了。

※改进和手工管理

这次我们再进行测试时就没有任何的问题了吗?如果只是上面的代码的确没有问题了,但问题是我们可能需要在任何地方使用接口操作我们所管理的对象,而delphi编译器会在接口变量离开作用域或者被手工设置为nil时自动调用_IntfClear以决定是否释放实现接口的对象,如果在这之前我们已经调用诸如DelAFoo这样的方法手工释放了我们的对象,那么在调用_IntfClear时,一个访问异常出现了,因为这时对象已经根本不存在了!!现在是应该彻底把对象生命周期交给我们管理而不是交给接口管理的时候了(上面的做法是片面的,因为事实上对象内部仍然存在着一个引用计数值,我们只是用了一点技巧混淆了它对对象生命周期的管理),看来我们又要回到最开始了,我们不得不考虑不要TinterfacedObject而是自己写一个实现Iinterface的类,彻底的抛弃引用计数,并省略到诸如BeforeDestruction之类的不必要的方法:

  TMyInterfacedObject = class(TObject, IInterface)

   protected

    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

    function _AddRef: Integer; stdcall;

    function _Release: Integer; stdcall;

上一页  [1] [2] [3]  下一页


[Delphi程序]非COM环境下的接口编程--问题,技巧,应用(二)  
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · C语言系列  · VB.NET程序
    · JAVA开发  · Delphi程序
    · 脚本语言
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台