原文
使用引用计数来管理对象的生命期,允许多个客户无需相互协调就获取并释放单个对象.
只要客户对象遵守某些使用规则,就会有效管理该对象.这些规则指定了如何管理对象间的引用.(COM不指定对象内部实现,尽管这些规则是对象内策略的合理起点.)
从概念上讲,可按在包含所有保存接口指针的内部计算状态的指针变量中驻留,来对待接口指针.包括内存位置中,内部处理器寄存器中,及程序生成的变量和编译器生成的变量.
赋值或初化指针变量,涉及创建现有指针的新副本.如果某个变量中有个(赋值/初化)指针副本中使用的值,则现在有两个.
与变量自身析构一样,赋值指针变量,也会析构变量中的指针副本.(也即,析构变量所在的域(如栈帧))
从COM客户角度来看,总是对每个接口执行引用计数.客户绝不应假定,对所有接口,对象使用相同的计数器.
默认是,必须为接口指针的每个新副本调用AddRef,且除非以下规则允许,必须对接口指针的每个析构调用Release:
1,函数的传入出(进出,inout)参数.当在参数上存储out值时,因为在调用Release释放它,来实现代码时,调用者必须在参数上调用AddRef.
2,取全局变量.从全局变量中的指针的现有副本创建接口指针的局部副本时,必须在局部副本上调用AddRef,因为在局部副本仍有效时,另一个函数可能会析构全局变量中的副本.
3,从"薄层"合成的新指针.使用特殊内部知识合成接口指针而不是从其他源取接口指针的函数,必须在新合成的指针,初始调用AddRef.
重要示例包括实例创建例程,QueryInterface的实现等.
4,提取内部存储指针的副本.函数提取由调用对象在内部存储指针的副本时,在函数返回前,该对象代码必须对指针调用AddRef.
提取指针后,原对象不能确定其生命期与指针内部存储副本生命期的关系.
默认情况的唯一例外,要求管理代码知道,对象上同一接口指针的多个副本的生命期关系,且只需通过禁止引用计数更改为零,来确保不会析构对象.
一般有如下两种情况:
1,如果已存在指针的一个副本,在第一个副本仍存在时,随后又创建了稍后析构的第二个副本,则可省略调用第二个副本的AddRef和Release.
2,如果存在指针的一个副本并创建了第二个副本,然后在第二个副本前析构了第一个副本,则可省略调用第二个副本的AddRef和第1个副本的调用Release.
下面是具体示例,前两种情况特别常见:
1,函数的输入(in)参数中.用来初化值的指针,有按参数传递给函数的接口指针的副本,因此不需要对参数使用单独引用计数.
2,函数包括返回值的输出(out)参数.要设置out参数,函数必须有接口指针的稳定副本.返回时,调用者负责释放指针.因此,out参数不需要单独引用计数.
3,局部变量.方法实现控制栈帧上分配的每个指针变量的生命期,并可用它来确定如何省略冗余的AddRef/Release对.
4,后指针.某些数据结构包含两个互指对象的指针的对象.如果已知第一个对象的生命期包含第二个对象的生命期,则无需引用计数第二个对象的指向第1对象的指针.
一般,避免循环引用对维护适当的释放行为非常重要.但是,使用未计数的指针时应格外小心,因为处理远程处理的操作系统部分无法了解此关系.
因此,最好让后指针看到第一个指针的第二个"友元"对象(从而避免循环).如,COM的可连接对象架构就是这样.
实现或使用引用计数对象时,应用人工引用计数也可能很有用,这样可保证函数处理过程中的对象稳定性.
实现接口方法时,可能会调用会减少对象引用计数的函数,从而导致过早释放对象从而失败.
可靠方法是在实现方法的开头插入调用AddRef,并在方法返回前调用配对的Release.
有时,AddRef和Release的返回值可能不稳定,不应依赖它;它们应仅用来调试或诊断.