COM引用计数-相互依赖对象

COM引用计数-相互依赖对象,com,atl,refcounting,Com,Atl,Refcounting,我有两个COM对象(我们称它们为Control和Job)。控件是可协同创建的,作业对象由Control.NewJob()创建 控件有一个方法Control.Start(job),该方法使指定的作业成为当前作业。只要未设置其他作业,它将保持当前作业 现在,对于客户端,以下行为对于这些特定控件来说似乎是合理的: 只要它的一个作业存在,控制就存在 (琐碎:作业对其创建的控件具有强引用) 只要客户机具有对控件或其当前作业的引用,都不会被销毁 (“平凡”:CurrentJob是一个强引用) 在发布引用之

我有两个COM对象(我们称它们为
Control
Job
)。控件是可协同创建的,作业对象由
Control.NewJob()
创建

控件有一个方法
Control.Start(job)
,该方法使指定的作业成为当前作业。只要未设置其他作业,它将保持当前作业

现在,对于客户端,以下行为对于这些特定控件来说似乎是合理的:

  • 只要它的一个作业存在,控制就存在
    (琐碎:作业对其创建的控件具有强引用)

  • 只要客户机具有对控件或其当前作业的引用,都不会被销毁 (“平凡”:CurrentJob是一个强引用)

  • 在发布引用之前,客户端不需要“清除”CurrentJob

现在,我这里有一个经典的循环引用,释放它的条件是两个对象都没有外部引用

我可以通过修改ATL的InternalRelease实现来解决这个问题,但这是非常丑陋和孤立的


有什么建议吗?现有解决方案

我认为ATL没有现成的解决方案,因为所讨论的行为对特定需求非常敏感

只要客户端有对控件或其CurrentJob的引用,都不会被销毁(“平凡”:CurrentJob是强引用)

此要求假设从当前作业的一侧存在对控件的外部强引用,因为控件只需要在客户端到作业引用中保持活动状态。也就是说,控件和作业彼此之间都有很强的引用关系。然后控件+作业组合需要正确处理所有外部引用的释放。例如,这可以通过以下方式实现

作业(与控件的方式相同?)被重写,它检查是否只剩下控件的外部引用。如果是这种情况,作业将启动终止检查-它将调用某些控件的方法来检查其引用。如果作业的引用是控件在自身上看到的唯一引用,则两个释放都会将一个引用到另一个(并终止)

只要它的一个作业存在,控制就存在

不,很确定这是你出错的地方。请注意,添加IControl::CreateJob()工厂函数的唯一原因是为CJob(实现类,而不是接口)提供一个CControl*引用。这意味着所有权,特定的IJob只能与特定的控件实例相关联。因此,CControl应保留其拥有的CJOB集合

现在变得直截了当:

  • CControl实例按常规创建,只有一个AddRef()调用,只有应用程序拥有该实例
  • IControl::CreateJob()实现方法使用CJob(Control*)构造函数创建一个新的CJob,将其添加到集合中并返回IJob接口指针。只有一个AddRef()调用,只有客户端应用程序拥有该实例
  • CJob析构函数调用私有CControl::RemoveJob()方法,以使用从其构造函数获得的CControl*将其自身从集合中移除(如果它不为null)
  • CControl析构函数迭代其集合,将任何剩余CJob维护的CControl*设置回null。它们现在是联合国所有的
  • IControl::Start(IJob*)实现方法调用AddRef()以确保作业保持活动状态。当作业完成或被替换或CControl对象被破坏时释放。请注意,需要进行错误检查,该方法需要迭代集合以找到匹配的CJob对象。因此,客户端无法启动由另一个控件实例创建的作业。并允许您将CJob*保留为CControl::currentJob私有变量

如何:在
AddRef
ing当前作业之后,控制
Release
s本身(以补偿作业持有的ref)。就在
释放
当前作业之前(当它被清除时,比如通过
启动(NULL)
),控制
添加参考
本身(以补偿以前的
释放
)。