iOS---NSAutoreleasePool自動釋放原理及詳解

前言:當您向一個對象發送一個autorelease消息時,Cocoa就會將該對象的一個引用放入到最新的自動釋放池。它仍然是個正當的對象,所以自動釋放池 定義的做用域內的其它對象能夠向它發送消息。當程序執行到做用域結束的位置時,自動釋放池就會被釋放,池中的全部對象也就被釋放。

1. ojc-c 是經過一種"referring counting"(引用計數)的方式來管理內存的, 對象在開始分配內存(alloc)的時候引用計數爲一,之後每當碰到有alloc,new,copy,retain的時候引用計數都會加一, 每當碰到release和autorelease的時候引用計數就會減一,若是此對象的計數變爲了0, 就會被系統銷燬.
2. NSAutoreleasePool 就是用來作引用計數的管理工做的,這個部分後面會詳細說到.
3. autorelease和release沒什麼區別,只是引用計數減一的時機不一樣而已,autorelease會在對象的使用真正結束的時候才作引用計數減一.
4.設定項目編譯環境爲ARC下時,編譯器會幫助咱們在程序的入口main函數就調用NSAutoreleasePool,這樣保證程序中不調用NSAutoreleasePool,但在退出時自動釋放

1.NSAutoreleasePool是什麼?
 NSAutoreleasePool其實是個對象引用計數自動處理器,在官方文檔中被稱爲是一個類。html

NSAutoreleasePool能夠同時有多個,它的組織是個棧,老是存在一 個棧頂pool,也就是當前pool,每建立一個pool,就往棧裏壓一個,改變當前pool爲新建的pool,而後,每次給pool發送drain消 息,就彈出棧頂的pool,改當前pool爲棧裏的下一個 pool。

2.NSAutoreleasePool能夠用來作什麼,怎麼用?程序員

NSAutoreleasePool能夠在必定程度上幫助咱們蘋果開發程序員管理內存,讓咱們的工做更加嚴密,簡便。多線程

  1)在ARC項目中,系統會自動幫助咱們在程序中嵌入NSAutoreleasePool,此爲蘋果公司程序員在寫這個編譯器的時候設定的;iphone

  2)在MRC項目中,咱們須要本身去建立NSAutoreleasePool類對象去幫助咱們管理內存;異步

  3)使用應注意:函數

      a.在ARC項目中咱們一樣能夠建立NSAutoreleasePool類對象去幫助咱們更精確的管理內存問題。oop

      b. NSAutoreleasePool的管理範圍是在NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];與[pool release];之間的對象測試

      c..既然ARC項目中設置了ARC,爲何還要使用@autoreleasepool?(注意a的案例解釋)優化

          ARC 並非捨棄了 @autoreleasepool,而是在編譯階段幫你插入必要的 retain/release/autorelease 的代碼調用。spa

          因此,跟你想象的不同,ARC 之下依然是延時釋放的,依然是依賴於 NSAutoreleasePool,跟非 ARC 模式下手動調用那些函數本質上毫無差異,只是編譯          器來作會保證引用計數的正確性。

          參考:      Retain count semantics in ARC

                   What's @autoreleasepool

    • d.NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
      當執行[pool autorelease]的時候,系統會進行一次內存釋放,把autorelease的對象釋放掉,若是沒有NSAutoreleasePool , 那這些內存不會釋放
      注意,對象並非自動被加入到當前pool中,而是須要對對象發送autorelease消息,這樣,對象就被加到當前pool的管理裏了。噹噹前pool接受到drain消息時,它就簡單的對它所管理的全部對象發送release消息。(如例子1)

       

    • e.在ARC項目中.不能直接使用autorelease pools,而是使用@autoreleasepool{},
      @autoreleasepool{}比直接使用NSAutoreleasePool效率高。不使用ARC的時候也可使用(autorelease嵌套

  4)使用例子:

例子1:

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString* nsstring;
char* cstring = "Hello CString";
nsstring = [NSString stringWithUTF8String:cstring];
[pool release];(這一行代碼就是在給pool發送drain消息了)

 









 

官方API摘抄翻譯:

3.autorelease的原理是什麼?

       Autorelease實際上只是把對release的調用延遲了,對於每個Autorelease,系統只是把該Object放入了當 前的Autorelease pool中,當該pool被釋放時,該pool中的全部Object會被調用Release。

4.!!!autorelease什麼時候釋放?

       對於autorelease pool自己,會在以下兩個條件發生時候被釋放(詳細信息請參見第5條)

1)、手動釋放Autorelease pool


2)、Runloop結束後自動釋放

       對於autorelease pool內部的對象在引用計數的retain == 0的時候釋放。release和autorelease pool 的 drain都會觸發retain--事件。

五、autorelease釋放的具體原理是什麼?


       要搞懂具體原理,則要先要搞清楚autorelease什麼時候會建立。

      
咱們的程序在main()調用的時候會自動調用一個autorelease,而後在每個 Runloop, 系統會隱式建立一個Autorelease pool,這樣全部的release pool會構成一個象CallStack同樣的一個棧式結構,在每個Runloop結束時,當前棧頂的 Autorelease pool(main()裏的autorelease)會被銷燬,這樣這個pool裏的每一個Object會被release。

      
能夠把autorelease pool理解成一個相似父類與子類的關係,main()建立了父類,每一個Runloop自動生成的或者開發者自定義的autorelease pool都會成爲該父類的子類。當父類被釋放的時候,沒有被釋放的子類也會被釋放,這樣全部子類中的對象也會收到release消息。

      
那什麼是一個Runloop呢? 一個UI事件,Timer call, delegate call, 一個鼠標事件,鍵盤按下(MAC OSX),或者iphone上的觸摸事件,異步http鏈接下後當接收完數據時,都會是一個新的Runloop。

      
通常來講,消息循環運行一次是毫秒級甚至微秒級的,所以autorelease的效率仍然是很是高的,確實是一個巧妙的設計。

六、使用有什麼要注意的?


1)、NSAutoreleasePool能夠建立一個autorelease pool,但該對象自己也須要被釋放,如:

  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init;
  2. // Code benefitting from a local autorelease pool.
  3. [pool release];
複製代碼


       在引用計數環境下,使用[pool release]或[pool drain]效果是相同的,drain僅適用於max os高版本,低版本不適用,而release通用,其它並沒有太大差異。

2)、在ARC下,不能使用上述方式調用autorelease,而應當使用@autoreleasepool,如:

  1. @autoreleasepool {
  2.    // Code benefitting from a local autorelease pool.
  3. }
複製代碼

3)、儘可能避免對大內存使用該方法,如圖片。對於這種延遲釋放機制,仍是儘可能少用,最好只用在方法內返回小塊內存申請地址值的狀況下,且參考和領會OC的一些系統方法,如:[NSString stringWithFormat:]。

4)、不要把大量循環操做放到同一個NSAutoreleasePool之間,這樣會形成內存峯值的上升。

七、關於多線程,有什麼要注意的?

        我還未實際使用到,在官方API翻譯出相似以下語句:

1)、對於不一樣線程,應當建立本身的autorelease pool。若是應用長期存在,應該按期drain和建立新的autorelease pool

       下面這句話摘自官方API,大概是說多線程中若是沒有使用到cocoa的相關調用,則不須要建立autorelease pool,我一直沒有理解透徹

If, however, your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.


2)、若是不是使用的NSThread,就不要用aoturelease pool,除非你是多線程模式(multithreading mode) ,可使用NSThread的isMultiThreaded方法測試你的應用是不是多線程模式

PS:

我把它理解爲:新開線程最好實現NSAutoreleasePool(當 咱們點擊一個App中的一個按鈕或者其餘能夠觸碰開啓新業務的UI控件,在程序裏面就會自動開啓一條新線程,當咱們不用這個業務的時候,就須要程序幫咱們 提早在「程序的主窗口的全部代碼編譯結束後」以前關閉這條線程以優化線程的使用,必定程度上儘可能避免線程開啓太多佔用CPU嚴重引發的卡頓問題)(業務邏 輯處理-業務線程優化)
詳細能夠參考官方API的NSAutoreleasePool Class Reference

相關文章
相關標籤/搜索