1. 程式人生 > >webkit技術譯文系列(二):RefPtr和PassRefPtr基礎

webkit技術譯文系列(二):RefPtr和PassRefPtr基礎

歷史

WebKit中的許多物件是引用計數的(reference counted),採用的模式就是類具有ref和deref成員函式增加和減少引用計數。每個ref呼叫必須有一個deref與之匹配。當在引用計數值為1的物件上呼叫deref方法時,物件刪除。WebKit中的許多類通過繼承RefCounted類模板應用該模式。

時間回溯到2005年,我們發現存在許多由於不正確呼叫ref和deref而引起的記憶體洩露,特別是HTML編輯的程式碼。

我們希望使用智慧指標來減少這一問題。但是早期的試驗表明智慧指標會進行額外的引用計數處理而影響效能。例如,一個函式有一個智慧指標的引數並返回該指標指標作為返回值,僅僅傳入該引數並返回該值會進行2到4次的增加和減少引用計數值,因為物件從一個智慧指標轉移到另一個上。因此我們尋找一種方式來讓我們使用智慧指標同時避免引用計數跳變(churn).

我們從C++標準類模板auto_ptr獲得靈感,這些物件實現了一種模型,賦值即是歸屬關係轉移(transfer of ownership),當您從一個auto_ptr賦值到另一個,貢獻值變成0.(When you assign from one auto_ptr to another, the donor becomes 0.)

Maciej Stachowiak設計了一對類模板,RefPtr和PassRefPtr,實現這一模式來解決WebKit中惱人的引用計數問題。

原始指標(Raw pointers)

當我們討論諸如RefPtr類模板之類的智慧指標時,通常使用原始指標(raw pointer)指代C++語言中內建的指標型別。下面是經典的使用原始指標(raw pointer)的設定函式:

 

RefPtr

RefPtr是一個簡單的智慧指標類,它對來值(incoming value)呼叫ref,對去值(outgoing value)呼叫deref。RefPtr可用於任何有ref和deref成員函式的物件。下面是使用RefPtr寫的設定函式例項:

 

單獨使用RefPtr可能會導致引用計數跳變(churn)。

 

出於討論考慮,我們假設節點物件的引用計數起始值為0(稍後更多),當它賦值給a後,引用計數值增加到1。建立返回值後引用計數值又增加到2,接下來a銷燬,引用計數值又減少到1。然後建立b後引用計數值增加到2,再下來createSpecialNode返回值銷燬後,引用計數值減到1。

(如果編譯器實現了返回值優化(return value optimization),就會少一次引用計數增加和減少。)

引用計數跳變(churn)在函式引數和返回值都涉及的情況下更嚴重,解決的方法就是PassRefPtr。

PassRefPtr

PassRefPtr和RefPtr相似,但存在一處不同,當您拷貝一個PassRefPtr或者賦PassRefPtr值給RefPtr或另一個PassRefPtr時,原來的指標值設定為0,操作不改變引用計數。讓我們來看一個新的版本的例子:

 

節點物件的初始引用計數值為0,當它賦值給a後,引用計數值增加到1