BREW(包括BUIW)引用计数及内存使用规则

来源:互联网 发布:linux 如何禁止ping 编辑:程序博客网 时间:2024/05/21 17:49

BREW(包括BUIW)引用计数及内存使用规则

毛晓冬 2007-1-26

 

一、概述:

BREW使用COM的引用计数机制在多个客户间同时共享一个接口的实例,保证各个客户在使用期间,该接口实例始终有效,而不管其他客户是否已经使用完该接口实例。只有当所有客户都结束使用时,该接口的实例才会被释放。

该机制使得我们必须正确操作接口的引用计数,否则会造成内存泄漏,或者重复释放等严重问题。

所以,我们需要掌握引用计数的使用规则和注意点。依据相关文档和SDK实例,总结本文档。

 

二、BREW标准接口的引用计数规则:

1. 规则1:当接口实例以函数的出口参数请求得到时,该接口实例已经在函数内部被引用计数加1

例子:Ishell_CreateInstancepIShellClsid(void**)&pInterface)创建接口,接口实例获得时,已经引用计数被加1

IXXX_QueryInterface(pIXXX,AEEXXID,(void**)&pInterface)查询接口,接口实例获得时,已经引用计数加1

IFORM_GetBGImage(IForm *po, IImage **ppi)获得Image接口实例时,Image接口实例引用计数已经被加1

2. 规则2:当接口实例以函数的返回值被请求得到时,该接口实例引用计数没有被加1

       例子:IWidget *ICONTAINER_GetWidget(IContainer *p, IWidget *pw, boolean d, boolean w)得到的Iwidget实例引用计数没有被加1 

3. 规则3:当接口实例以函数的输入参数输入时,该接口实例会被函数内部引用计数加1

例子:void IDECORATOR_SetWidget(IDecorator *p, IWidget *pw)的内部会将pw实例的引用计数加1

 

三、一些例外:

1. Ibitmap*Idisplay_GetDestination(Idisplay *po)Ibitmap* Ishell_LoadImage()会将返回的Ibitmap接口实例引用计数加1

2. Ifont*Idisplay_SetFont(Idisplay* po,AEEFont nFont,Ifont* piNewFont),即不会将返回的Old Font接口实例引用计数加1,也不会将输入的NewFont接口实例引用计数加1

 

四、如何确切知道引用计数行为:

SDK90%以上的接口函数,都遵循标准引用计数规则,只有极少数的接口函数属于例外。但都在SDK的接口函数说明中详细进行了说明。所以,调用一个接口函数时,确切的了解对接口实例是否进行了引用计数加1的最好方法,是阅读相关的SDK说明。

 

五、使用接口时,我们该怎么做:

基于以上的引用计数规则,我们在使用接口时,应该注意:

1. 当调用某些接口函数请求接口时,如果接口实例被引用计数加1了,那么请求者有责任当使用完毕后,调用接口的Release进行释放。

否则,请求者只需要使用,无需负责释放(这会造成接口的两次释放)。如果此时,请求者想异步的长时间使用该接口,为了避免其他地方的释放对己造成的影响,请求者可以在请求得到该接口实例后,先调用AddRef,然后再使用,使用结束后,调用Release

2. 当调用某些接口函数输入接口时,如果接收接口的函数内部做了引用计数加1,那么输入者输入完接口后,可以在任何想释放的时候释放该接口实例,因为接收组件内部会管理该实例。

否则,输入者输入完接口后,仍然必须一直保证该接口的实例有效,不能过早的释放。

例子:App使用Idisplay_SetFont时,输入pNewFont的实例后,必须一直保证该实例的有效性,不能将其释放,因为Idisplay_SetFont内部并没有对pNewFont的引用计数加1

 

六、一些延伸:

基于上面的引用计数使用规则和注意点。我们可以延伸到其他的使用注意点:

1. App间以参数的形式进行接口实例的传递时,接收方必须将其引用计数加1后,再使用,使用完后,再Release,发送方将不再对传出的接口实例负责。

2. 自己实现扩展接口时,也必须遵循相关的规则,比如当接收接口实例时,需要进行引用计数加1后再使用,使用完后进行释放。以出口参数带出接口实例时,必须先引用计数加1,再带出。

 

七、最后一个注意点:

由于引用计数的存在,接口被Release时可能仅仅是引用计数减1,而非真正的被释放。所以,不能轻易的在释放接口后,将接口实例指针置为NULL,这是和一般的内存操作不一样的。错误的将接口实例指针置为NULL,可能会导致内存泄漏或者非法指针操作等潜在的问题。

一般的准则是,只有当确保该接口实例不会再有任何组件使用时,才将其置为NULL。比如,对于App Struct中的接口成员,应该只在Free Data中使用RELEASEIF释放并置为NULL,其他处,应该只进行释放,而不置为NULL。对于Form的成员,则可以在FormDelete Data回调中,使用RELEASEIF释放并置为NULL,其他处,应该只进行释放,而不置为NULL

注意:RELEASEIF是宏,释放接口后,会将其置为NULL

原创粉丝点击