1

我有两个 COM 对象(我们称它们为ControlJob)。Control 是 CoCreatable,Job 对象由Control.NewJob().

Control 有一种方法Control.Start(job)可以使指定的作业成为当前作业。只要没有设置其他作业,它就一直是当前作业。

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

  • 只要其中一个作业存在,控制就存在
    (微不足道:作业对它创建的控制持有强引用)

  • 只要客户端有对 Control 或其 CurrentJob 的引用,它们都不会被销毁(“微不足道”:CurrentJob 是一个强引用)

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

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

可以通过处理 ATL 的 InternalRelease 实现来解决这种情况,但这非常丑陋和孤立。

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

4

2 回答 2

2

只要其中一个作业存在,控制就存在

不,相当肯定这是你出错的规则。请密切注意,添加 IControl::CreateJob() 工厂函数的唯一原因是为 CJob(实现类,而不是接口)提供 CControl* 引用。这意味着所有权,一个特定的 IJob 只能与一个特定的 Control 实例相关联。因此,CControl 应该保留它拥有的 CJobs 集合。

它现在变得直截了当:

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

我认为 ATL 没有开箱即用的解决方案,因为所讨论的行为对特定要求很敏感。

只要客户端有对 Control 或其 CurrentJob 的引用,它们都不会被销毁(“微不足道”:CurrentJob 是一个强引用)

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

Job 的(和 Control 的一样?)CComObjectRootEx::InternalRelease被覆盖,它检查唯一的外部引用是否是控件的引用。如果是这种情况,则作业正在启动终止检查 - 它调用某些控件的方法来检查其引用。如果作业的引用是唯一的引用控件在其自身上看到,那么两个释放引用一个到另一个(并终止)。

于 2014-10-23T11:10:48.260 回答