引用计数 nRefs 说明和使用 | developer.brewmp.com 引用计数 nRefs 说明和使用 | developer.brewmp.com

Developer

引用计数 nRefs 说明和使用

Forums:

BREW接口包含了面向对象的概念,所有接口都以IBase为基类,而IBase定义了最基本的两个方法:IBase_AddRefIBase_Release

这两个方法都和一个变量紧密关联,这就是 nRefs - 引用计数。每个接口(且称之为AAA)的AAA_AddRef()的实现就是将引用计数加一:

  ++me->nRefs

而每个接口对象的释放函数设计都如下面模式:

  uint32 AAA_Release(void *po)

  {

    if (me->base.nRefs == 1)

    {

      AAA_Dtor(me); // 资源释放调用    }

    return --me->nRefs;

  }

可见只有当引用计数降到1时,才真正释放资源。

很少情况下会显式地调用IBase_AddRef(),这意味着在释放时要调用AAA_Release()多次才能真正释放资源。

引用计数的意义在于,可能有多个其它关联对象同时需要使用此对象的资源,关联对象与此对象的关系可能是包含关系,有可能是继承关系,引用计数反映了这些关联对象的个数,只有所有关联对象都释放了,此对象资源才真正被释放。

一个对象的引用计数都是隐含地增加的,即对象的AAA_AddRef()都是被隐含地透明地调用,调用发生在与关联对象相互作用之时,BUIW设计中经常在下面四种情况下隐含地调用AAA_AddRef()。

(一). 容器(container)获取其widget时

  以根容器为例,创建根容器并获取其widget的代码如下:

  ISHELL_CreateInstance(pMe->pIShell, AEECLSID_ROOTCONTAINER, (void**)&pMe->picRoot);

  IROOTCONTAINER_QueryInterface(pMe->picRoot, AEEIID_WIDGET, (void**)&pMe->piwRoot);

  其中pMe->piwRoot与pMe->picRoot的关系是组合关系,即pMe->picRoot包含pMe->piwRoot,pMe->picRoot创建时,其中的widget对象的引用计数已经是1,当IROOTCONTAINER_QueryInterface()将widget取出单独使用时,widget的引用计数加一,即为2,所以在释放资源时,需要调用如下:

  RELEASEIF(pMe->picRoot);

  RELEASEIF(pMe->piwRoot);

前一句释放根容器资源时也同时试图释放其widget资源,但其效果仅仅是将widget引用计数减一,到第二句才真正释放widget资源。

(二). 向容器(container)插入widget时容器往往管理多个widget,管理关系的建立需要首先将widget插入到容器中:

  (void)IXYCONTAINER_Insert(pMe->pXYContainer, piwStaticHello, WIDGET_ZNORMAL, &dwpos);

  // 随后在本函数中释放临时变量 piwStaticHello

  RELEASEIF(piwStaticHello);

实际上,调用IXYCONTAINER_Insert()时,piwStaticHello的引用计数增一,随后的释放(RELEASEIF(piwStaticHello))并没有将piwStaticHello指向的对象资源释放掉,仅仅是引用计数减一,而piwStaticHello指向的对象资源释被pMe->pXYContainer管理且跟随它被释放。

(三). 向修饰器(container)设置被修饰widget时

修饰器pid修饰widget ppiw时,需要调用

  IDECORATOR_SetWidget(pid, *ppiw);

  RELEASEIF(*ppiw);

ppiw在调用IDECORATOR_SetWidget()之前,引用计数可能为1,调用IDECORATOR_SetWidget()则增一,变为2,RELEASEIF(*ppiw)后,引用计数减为1,但是对象资源没有释放,而是被修饰器管理并跟随它被释放。

(四). widget设置model时

widget与model各自创建,然后使用下面语句将model设置到widget

  IWIDGET_SetModel(piw, IVECTORMODEL_TO_IMODEL(pivm));

设置完成意味着model被widget管理,model pivm创建时引用计数为1,调用IWIDGET_SetModel()后引用计数为2,那么如想完全释放model对象资源,需要调用下面两个释放操作:

    RELEASEIF(pivm);

    RELEASEIF(piw);

缺少任意一个,都不会将pivm引用计数降为0,从而不会真正释放pivm资源,造成内存泄漏。