BREW(包括BUIW)引用计数及内存使用规则
2010-06-13 10:13
357 查看
BREW(包括BUIW)引用计数及内存使用规则
毛晓冬 2007-1-26
一、概述:
BREW使用COM的引用计数机制在多个客户间同时共享一个接口的实例,保证各个客户在使用期间,该接口实例始终有效,而不管其他客户是否已经使用完该接口实例。只有当所有客户都结束使用时,该接口的实例才会被释放。
该机制使得我们必须正确操作接口的引用计数,否则会造成内存泄漏,或者重复释放等严重问题。
所以,我们需要掌握引用计数的使用规则和注意点。依据相关文档和SDK实例,总结本文档。
二、BREW标准接口的引用计数规则:
1.
规则1:当接口实例以函数的出口参数请求得到时,该接口实例已经在函数内部被引用计数加1。
例子:Ishell_CreateInstance(pIShell,Clsid,(void**)&pInterface)创建接口,接口实例获得时,已经引用计数被加1。
IXXX_QueryInterface(pIXXX,AEEXXID,(void**)&pInterface)查询接口,接口实例获得时,已经引用计数加1。
2.
规则2:当接口实例以函数的返回值被请求得到时,该接口实例引用计数没有被加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。
四、如何确切知道引用计数行为:
SDK中90%以上的接口函数,都遵循标准引用计数规则,只有极少数的接口函数属于例外。但都在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的成员,则可以在Form的Delete Data回调中,使用RELEASEIF释放并置为NULL,其他处,应该只进行释放,而不置为NULL。
注意:RELEASEIF是宏,释放接口后,会将其置为NULL。
毛晓冬 2007-1-26
一、概述:
BREW使用COM的引用计数机制在多个客户间同时共享一个接口的实例,保证各个客户在使用期间,该接口实例始终有效,而不管其他客户是否已经使用完该接口实例。只有当所有客户都结束使用时,该接口的实例才会被释放。
该机制使得我们必须正确操作接口的引用计数,否则会造成内存泄漏,或者重复释放等严重问题。
所以,我们需要掌握引用计数的使用规则和注意点。依据相关文档和SDK实例,总结本文档。
二、BREW标准接口的引用计数规则:
1.
规则1:当接口实例以函数的出口参数请求得到时,该接口实例已经在函数内部被引用计数加1。
例子:Ishell_CreateInstance(pIShell,Clsid,(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。
四、如何确切知道引用计数行为:
SDK中90%以上的接口函数,都遵循标准引用计数规则,只有极少数的接口函数属于例外。但都在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的成员,则可以在Form的Delete Data回调中,使用RELEASEIF释放并置为NULL,其他处,应该只进行释放,而不置为NULL。
注意:RELEASEIF是宏,释放接口后,会将其置为NULL。
相关文章推荐
- Delphi结构中使用String时遇到的内存泄露问题(没有利用String的引用计数自动销毁字符串的功能)
- 有效的使用和设计COM智能指针 ——条款16:智能指针的引入不能违反COM引用计数规则
- 14-ARC自动引用内存使用及管理机制
- C++ 引用 是否占内存和使用
- C中内存使用规则
- 使用引用的规则:
- openscenegraph引用计数指针使用注意事项
- 有效的使用和设计COM智能指针——条款12:必要时使用attach() 和 detach()调整引用计数
- Block使用中的一些疑问解答(包括循环引用问题的解决)
- ACE反应器框架引用计数策略的使用
- 基于Java软引用机制最大使用JVM堆内存并杜绝OutOfMemory
- objective-C引用计数的使用心得
- 引用计数基本知识 & PHP 的内存泄露
- C++如何使用简单的引用计数
- COM的引用计数规则
- cocos2d-x 从onEnter、onExit、 引用计数 谈内存泄露问题
- 使用Handler容易产生的内存泄露以及介绍下Java的4种引用
- 自动引用计数(ARC)必须遵守一些规则
- [深入分析BREW机制]:BREW接口的引用计数机制
- 非静态内部类持有外部类的引用 使用不慎会造成内存溢出