當前位置:編程學習大全網 - 遊戲軟體 - 搞懂Objective-C中的autorelease

搞懂Objective-C中的autorelease

本文是上壹篇: 搞懂Objective-C中的ARC 的延伸和補充

操作系統實現了壹個線程工廠稱為線程池,還有壹個配合工作的管理器稱為調度中心,iOS系統並沒有接口去直接操作線程池,線程的創建、調度、銷毀全權交給線程池,當我們試圖去創建壹個線程的時候,調度中心會 檢查線程池的運行狀態、運行線程數、運行策略,決定接下來的執行流程,是直接申請線程執行還是放入緩沖隊列中,待到執行的時候,線程池決定是開辟新的線程還是復用現有空閑線程,長時間空閑的線程會被回收 (壹般10s左右)

iOS操作系統為 線程運行 配備了壹套數據結構, 壹個棧、壹個autoreleasePool、壹個runloop(懶加載) 以及壹些用於控制狀態的標誌位變量

好的~我們回到問題本身

先說結論:

再看個demo,這裏只討論非主線程:

如上創建了壹個串行隊列,寫了個死循環,壹直在創建堆區字符串,但是內存不會漲壹絲壹毫,原因上壹篇文章 搞懂Objective-C中的ARC 講過,這個對象直接會被release,不會加入autoreleasePool

修改壹下:

會發現非主線程創建autorelease對象會導致內存無休止的在漲,直到OOM

再次修改:

結果是內存也不會漲壹絲壹毫,數據剛好能對應上面的結論,對於有知其所以然需求的可以往下看

前輩 這篇 講得很好了,本文會基於最新源碼做個淺析(可能不是最新源碼,作者開始看源碼時候的版本objc4-781.2),順便講述下如何查閱源碼, 源碼鏈接

會發現AutoreleasePoolPage是壹個c++的類,繼承AutoreleasePoolPageData,具體實現很長,這裏省去部分函數實現

還是很長是不是,但是對於理解原理都重要,看看蘋果自己的註釋

/***********************************************************************

Autorelease pool implementation

A thread's autorelease pool is a stack of pointers.

Each pointer is either an object to release, or POOL_BOUNDARY which is

an autorelease pool boundary.

A pool token is a pointer to the POOL_BOUNDARY for that pool. When

the pool is popped, every object hotter than the sentinel is released.

The stack is divided into a doubly-linked list of pages. Pages are added

and deleted as necessary.

Thread-local storage points to the hot page, where newly autoreleased

objects are stored.

**********************************************************************/

/***********************************************************************

Autorelease pool 的實現

線程的Autorelease pool是壹系列聲明在棧上的指針。

每個指針要麽是壹個需要釋放的對象,要麽是 POOL_BOUNDARY,它是

Autorelease pool邊界。

token是指向該池的 POOL_BOUNDARY 的指針。 什麽時候

池被彈出,每個比哨兵更熱的對象都被釋放。

棧被分成壹個雙向鏈接的AutoreleasePoolPage列表。 Pages根據需要,添加

和刪除。

線程本地存儲指向最新被存儲到自動釋池所在的熱點頁面

**********************************************************************/

看完這個註釋,我覺得都不用往下講了?,壹個字,清晰透徹

next指針作為遊標指向棧頂最新add進來的autorelease對象的下壹個位置,當有新的obj加入autoreleasePool的時候取到next指針的地址存儲obj,next指針向棧頂方向移動8個字節

每個autoreleasePool初始化的時候都會綁定當前線程,如下是AutoreleasePoolPage的初始化函數

第壹個參數是AutoreleasePoolPage的標識,第二個參數是壹個函數指針,線程退出之前會執行這個函數,就是tls_dealloc,調用棧如下:

線程休眠之前,runloop會調用objc_autoreleasePoolPop,上面流程除了第壹步,後面壹樣

單向的可以嗎?可以呀,但是查詢速度會以指數級別降低,刪除棧頂page後想刪除的倒數第二個page,怎麽辦,從頭開始遍歷唄,遍歷n-1次,然後遍歷n-2次.......

所以為什麽設計成雙向鏈表,效率高,如此而已

  • 上一篇:椿兮如雨湫兮如風這兩句話的含義
  • 下一篇:何以笙簫默何以琛趙默笙全集百度雲
  • copyright 2024編程學習大全網