引用計數法又是什麼鬼呢? 顧名思義, 對物件的引用進行計數. 通過記錄每個物件被引用的次數, 來確定這個物件是否可以被**.
首先, 對物件的引用數量進行管理, 什麼時候會更新呢?
建立物件: 新建乙個物件(對這個新的物件引用數量+1)
更新指標: 將乙個指向a物件的指標重新指向b物件(將a物件引用數量-1, b物件引用數量+1)
這次就不上**了, 簡單介紹一下思路就行. (我哥說**看著費勁)
前提: 我們有乙個全域性的空閒位址鍊錶: free_head
建立物件的操作
從free_head中尋找記憶體
若找到了, 該物件計數器置為1, 返回
若沒有找到, 記憶體擴容, 返回1
更新指標的操作
將新的物件引用計數+1
將舊的物件引用計數-1. 若-1後引用數量為0, 則將該物件及所有的子物件新增到free_head
鍊錶中.
實現說起來簡簡單單, 畢竟我也不用真的去實現, 簡單想一下.
在上一次的標記清除演算法
中, gc在每次記憶體不足時執行, 勢必會導致程式暫停時間比較長. 但引用計數
則在每次指標變更的同時進行管理, 在產生新的垃圾的時候立刻進行**. 這就體現出它的幾個優勢了:
最大暫停時間短.
產生垃圾可立即**
當然, 只說優勢不說劣勢都是扯犢子. 首先,引用計數
的優勢也會成為它的劣勢, 計數頻繁的計算, 會拖累程式的速度. 而且每個物件都要開拓空間來儲存引用數量. 當然了, 還有經常被說到的迴圈引用的問題. 等等吧.
頻繁的更新引用計數拖累程式速度
每個物件需要開拓額外空間儲存引用計數
迴圈引用物件無法被**(就是a引用b, b引用a. 但是他們都沒有被其他物件引用, 導致他倆的引用始終為1, 無法**)
當然, 針對問題, 偉大的前人總是有辦法去解決. 比如:
延遲計數法: 針對頻繁更新計數器的問題而提出的. 大概意思就是不去實時的對引用數量進行更新, 將引用數量為0的記錄到乙個待處理的鍊錶中, 當需要新的記憶體時再統一處理. 但是這樣又會增大暫停時間, 才不要.
sticky引用計數法: 引用計數通過額外的空間儲存引用數量, 但這個必然會有最大值, 比如用1個位元組, 則引用數量超過256的就記不下了. 這個方法對超出範圍的處理方式很簡單, 什麼都不做, 不去**, 畢竟被引用這麼多次, 該物件定然很重要. 那這些物件不就永遠都不能被**了麼? 可以, 等到沒有記憶體了, 使用標記清除演算法
將所有物件過一遍.
當然, 針對引用計數法還有很多演變, 有些還是很有意思的, 有些是我看不懂的.
垃圾**的整體思路分兩個流派(我所知道的):
引用計數: 就是上面說的這種
可達性: 就是標記清除
那種, 判斷乙個物件是否可以到達.
引用計數
的最大優勢應該就是不需要暫停程式去進行**了, 隨使用隨**. 但劣勢也很明顯: 需要計數器額外空間以及迴圈引用的問題.
個人是比較喜歡引用計數
的, 實時性又高, 又不需要太多的額外空間. 只是需要在編寫**的時候刻意規避迴圈引用, 或者其他方法規避一下? 甚至不去處理都刻意, 如果只有少數的話(如果有很多, 還是換個演算法吧).
自動引用計數
每當你產生乙個物件,arc分配一大塊控制項去儲存這個物件。除此之外,當乙個物件是不在需要,arc 將會收回這個例項物件所佔的空間。如果乙個物件的空間被釋放掉,但是你任然通過物件的引用呼叫方法或者屬性,你的程式將要崩潰。為了不讓物件被釋放掉 arc將會追蹤有多少屬性 常量 變數正在引用這個物件。只要有...
引用計數 copy on write
拷貝通常分為值拷貝也叫淺拷貝和深拷貝,當拷貝方式為淺拷貝時就會有以下兩個問題 1 當需要調析構函式時,會出現同一塊空間被析構兩次的情況 2 如果是對指標的淺拷貝,會讓兩個指標同時管理同一塊空間,當乙個指針對這塊空間進行修改時,會影響到另外乙個指標。通過引用計數加寫時拷貝可以有效地解決這些問題。引用計...
自動引用計數
使用弱引用解決迴圈強引用 如果兩個變數屬性都可以設定為nil,那麼就用弱引用來設定其中乙個屬性來解決迴圈強引用。class person weak var apartment apartment?deinit class apartment deinit var aperson person?per...