iOS-autorelease與autoreleasepool
Autoreleasepool自動釋放池塊提供了一個持有物件的所有權的機制,可以避免它立刻釋放(如你從一個方法返回一個物件時).正常情況下,我們不需要建立自己的自動釋放池塊,但也有一些情況下,建立自動釋放池是非常明智的(子執行緒開啟新的任務,for迴圈生成大量物件的時候).
autorelease 與 runloop
autorelease 本質上就是延遲呼叫 release,實際上autorelease物件是在當前的runloop迭代結束時釋放的,以下是在iOS 8.2模擬器中的測試程式碼:
<pre><code>`_weak id reference = nil;
(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSArray *arr = [NSArray arrayWithObjects:@"FlyElephant",@"Keso", nil];
reference = arr;
}(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"陣列:%@",reference);
}(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"陣列:%@",reference);
}`</code></pre>
加入autorelease的測試程式碼:
<pre><code>@autoreleasepool { NSArray *arr = [NSArray arrayWithObjects:@"FlyElephant",@"Keso", nil]; reference = arr; }
</code></pre>
autoreleasepool 與 runloop
autoreleasepool與runloop乍一看沒有關係,如果對Runloop有研究,對下面這段文字應該有印象:
App啟動後,蘋果在主執行緒 RunLoop 裡註冊了兩個 Observer,其回撥都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一個 Observer 監視的事件是 Entry(即將進入Loop),其回撥內會呼叫 _objc_autoreleasePoolPush() 建立自動釋放池。其 order 是-2147483647,優先順序最高,保證建立釋放池發生在其他所有回撥之前。
第二個 Observer 監視了兩個事件: BeforeWaiting(準備進入休眠) 時呼叫_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池並建立新池;Exit(即將退出Loop) 時呼叫 _objc_autoreleasePoolPop() 來釋放自動釋放池。這個 Observer 的 order 是 2147483647,優先順序最低,保證其釋放池子發生在其他所有回撥之後。
在主執行緒執行的程式碼,通常是寫在諸如事件回撥、Timer回撥內的。這些回撥會被 RunLoop 建立好的 AutoreleasePool 環繞著,所以不會出現記憶體洩漏,開發者也不必顯示建立 Pool 了。
autoreleasepool 原理
ARC下我們使用@autoreleasepool{}來使用一個AutoreleasePool,隨後編譯器將進行編譯:
<pre><code>void *context = objc_autoreleasePoolPush(); // {}中的程式碼 objc_autoreleasePoolPop(context);
</code></pre>
autoreleasepool最終底層是由autoreleasepoolpage實現,定義如下:
<pre><code>class AutoreleasePoolPage { magic_t const magic; id *next; pthread_t const thread; AutoreleasePoolPage * const parent; AutoreleasePoolPage *child; uint32_t const depth; uint32_t hiwat; };
</code></pre>
1.autoreleasePool並沒有單獨的結構,而是由若干個AutoreleasePoolPage以雙向連結串列的形式組合而成(分別對應結構中的parent指標和child指標)
2.AutoreleasePool是按執行緒一一對應的(結構中的thread指標指向當前執行緒)
3.AutoreleasePoolPage每個物件會開闢4096位元組記憶體(也就是虛擬記憶體一頁的大小
4.next指標作為遊標指向棧頂最新add進來的autorelease物件的下一個位置
5.一個AutoreleasePoolPage的空間被佔滿時,會新建一個AutoreleasePoolPage物件,連線連結串列,後來的autorelease物件在新的page加入.
autoreleasepool 實戰
正常開發中也沒有見過哪個專案中到處都是autoreleasepool的,那麼什麼時候使用autoreleasepool呢?
1.如果你寫的程式不是基於UI框架的,比如說命令列工具.(較少)
2.如果建立一個迴圈,建立了大量的臨時物件,你可以使用自動釋放池處理在下一次迭代前處理這些物件,避免佔用大量記憶體.
iOS中有三種迴圈比遍歷方式for、forin、enumerateObjectsUsingBlcok,實際上enumerateObjectsUsingBlcok內部已經通過@autoreleasepool{}操作進行了物件處理,for和forin的方式需要我們自己手動處理,因此enumerateObjectsUsingBlcok效率最高,記憶體佔用最少.
3.建立次級執行緒,當你建立執行緒的時候,你需要建立釋放器避免記憶體洩漏.
Each thread in a Cocoa application maintains its own stack of autorelease pool blocks. If you are writing a Foundation-only program or if you detach a thread, you need to create your own autorelease pool block.
If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should use autorelease pool blocks (like AppKit and UIKit do on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If your detached thread does not make Cocoa calls, you do not need to use an autorelease pool block.
在Cocoa應用程式中每個系統執行緒都有自己的自動釋放池來維護,如果手動建立建立執行緒,需要手動建立自動釋放池.
如果建立常駐執行緒可能會導致大量的autorelease物件,應該像AppKit和UIkit一樣使用autoreleasepool,如果你不使用cocoa的建立執行緒(比如通過POSIX建立執行緒),那麼不需要使用autoreleasepool.
參考資料
官方文件
黑幕背後的Autorelease
深入理解RunLoop
Objective-C Autorelease Pool 的實現原理
相關文章
- AutoreleasePool分析整理
- AutoreleasePool的實現
- iOS 深入探究 AutoreleasePooliOS
- Runtime原始碼 autoreleasepool原始碼
- AutoreleasePool、Block、Runloop整理筆記BloCOOP筆記
- AutoreleasePool底層實現原理
- 關於autoreleasepool記憶體管理記憶體
- 探究是否需要@autoreleasepool優化迴圈優化
- 在ARC環境中autoreleasepool(runloop)的研究OOP
- undefined與null與?. ??UndefinedNull
- &與&&, |與||區別
- 進與穩,時代與技術,新基建與華為雲
- 程式與執行緒、同步與非同步、阻塞與非阻塞、併發與並行執行緒非同步並行
- Promise與async/await與GeneratorPromiseAI
- in與exist , not in與not exist 的區別
- Python學習筆記 5.0 元組 與 字典 與 集合 與 公共操作 與 推導式Python筆記
- 聊聊執行緒與程式 & 阻塞與非阻塞 & 同步與非同步執行緒非同步
- jQuery與JavaScript與ajax三者的區別與聯絡jQueryJavaScript
- forms元件補充與ModelForm簡單使用與cookie與sessionORM元件CookieSession
- PHP 與 Swoole 淺析與學習PHP
- PySpark與GraphFrames的安裝與使用Spark
- PHPCookie與Session的使用與區別PHPCookieSession
- 同步與非同步 阻塞與非阻塞非同步
- define與typedef區別與聯絡
- WAS與IHS整合的安裝與配置
- Iterator與Iterable(迭代器與可迭代)
- 漏型與源型、PNP與NPN
- js == 與 ===JS
- Process與Socket,Select與Accept關係
- cookie與session的區別與聯絡CookieSession
- Java:運用while()與do....while與for()JavaWhile
- 陣列與字串方法與相互轉換陣列字串
- Session與Cookie的區別與聯絡SessionCookie
- cookie與session的自己思考與解釋CookieSession
- GRPC與 ProtoBuf 的理解與總結RPC
- 同步與阻塞的區別與聯絡
- 關於 in與exist , not in與not exist 的區別
- rpm與yum,at與crontab,sed命令使用