管理參考計數的規則

使用參考計數來管理物件的存留期可讓多個用戶端取得並釋放單一物件的存取權,而不需要與彼此協調來管理物件的存留期。 只要客戶端物件符合特定使用規則,物件就會提供這項管理。 這些規則會指定如何管理對象之間的參考。 (COM 不會指定對象的內部實作,雖然這些規則是對象內原則的合理起點。

就概念上講,介面指標可以視為位於包含介面指標的所有內部計算狀態的指標變數內。 這包括記憶體位置、內部處理器緩存器中的變數,以及程式設計人員產生的變數和編譯程式產生的變數。 指標變數的指標變數的指標或初始化牽涉到建立現有指標的新複本。 其中某個變數中有一個指標復本(指派/初始化中使用的值),現在有兩個。 指標變數的指派會終結變數中目前存在的指標複本,如同變數本身的解構一樣。 (也就是說,找到變數的範圍,例如堆疊框架,會終結。

從 COM 客戶端的觀點來看,每個介面一律會執行參考計數。 用戶端絕對不應該假設物件對所有介面使用相同的計數器。

默認案例是,必須針對介面指標的每個新複本呼叫 AddRef,而且必須針對介面指標的每個解構呼叫 Release,除非下列規則允許否則:

  • 函式的輸出參數。 呼叫端必須在 參數上呼叫 AddRef,因為當 out 值儲存在參數上方時,它會在實作程式代碼中釋放它(使用 Release 呼叫)。
  • 擷取全域變數。 從全域變數中指標的現有複本建立介面指標的本機複本時,您必須在本機複本上呼叫 AddRef ,因為另一個函式可能會在本機複本仍然有效時終結全域變數中的複本。
  • 從「薄空氣」合成的新指標。使用特殊內部知識合成介面指標的函式,而不是從其他來源取得介面指標,一開始必須在新合成的指標上呼叫 AddRef 這類例程的重要範例包括實例建立例程、QueryInterface 的實作等等。
  • 擷取內部儲存指標的複本。 當函式擷取由所呼叫對象內部儲存的指標複本時,該物件的程式代碼必須先在指標上呼叫 AddRef ,函式才會傳回。 擷取指標之後,原始物件就沒有其他方式判斷其存留期與指標內部儲存複本的關聯性。

默認案例的唯一例外狀況是,管理程式代碼必須知道物件上相同介面之兩個或多個指標復本的存留期關聯性,而且只要允許對象的參考計數移至零,即可確保物件不會終結。 通常有兩種情況,如下所示:

  • 當指標的一個複本已經存在,而第二個複本之後就會建立,然後在第一個復本仍然存在時被終結時,可以省略對第二個複本的 AddRef Release 呼叫。
  • 當指標的一個複本存在且第二個複本建立,然後第一個複本在第二個複本之前終結時,可以省略第一個複本的 AddRef 呼叫Release。

以下是這些情況的特定範例,前兩個特別常見:

  • 在函式的參數中。 作為參數傳遞至函式之介面指標複本的存留期會巢狀於用來初始化值的指標中,因此不需要參數上的個別參考計數。
  • 從函式輸出參數,包括傳回值。 若要設定 out 參數,函式必須有介面指標的穩定複本。 傳回時,呼叫端會負責釋放指標。 因此,out 參數不需要個別的參考計數。
  • 區域變數。 方法實作可控制堆疊框架上配置之每個指標變數的存留期,而且可以使用此方法來判斷如何省略備援 AddRef/Release 配對。
  • Backpointers。 某些數據結構包含兩個 物件,每個物件都有另一個的指標。 如果已知第一個物件的存留期包含第二個物件的存留期,就不需要在第二個物件的指標上擁有參考計數。 通常,避免此迴圈對於維護適當的解除分配行為很重要。 不過,由於處理遠端處理的操作系統部分無法知道此關聯性,因此應該謹慎使用未計分的指標。 因此,在幾乎所有情況下,讓背點器看到第一個指標的「friend」物件(因此避免迴圈性)是慣用的解決方案。 例如,COM 的可連接物件架構會使用此方法。

實作或使用參考計數物件時,套用 人工參考計數可能很有用,這可保證函式處理期間的物件穩定性。 在實作介面的方法時,您可以呼叫有機會將參考計數遞減至 物件的函式,導致物件過早釋放,以及實作失敗。 避免這種情況的健全方式是在方法實作的開頭插入對 AddRef 的呼叫,並將它與方法傳回前的 Release 呼叫配對。

在某些情況下,AddRef Release傳回值可能不穩定且不應該依賴;它們只應該用於偵錯或診斷用途。

透過參考計數管理物件存留期