爲了開學的面試,就在博客裏總結一下面試會問到的問題,今天就來談談內存管理,看到一篇文章很是不錯,http://vinceyuan.cnblogs.com/,深刻淺出,推薦你們去看看!java
Objective-C使用一種(Retain Count)引用計數的機制來管理內存,在OC中,每一個對象都持有本身的retain count,引用計數能夠理解爲就是一個計數器,當對象alloc建立的時候,會自動設置爲1,當給對象發送retain消息的時候,引用計數會加1,當給對象發送release消息的時候,引用計數會減1,當引用計數爲0的時候,對象會釋放所佔用的內存,這就是內存管理的機制,聽起來比較容易吧,下面就進一步分析這種機制。ios
首先,咱們應該知道爲何要這樣作?咱們常常在不一樣的對象中引用相同的對象,例如:假設咱們用不一樣電腦遠程鏈接到同一臺服務器進行遠程操做,轉化成OC語言就是不一樣電腦對象引用相同的服務器對象,這時候有一臺電腦在服務器上敲了shutdown命令,讓服務器掛掉了,這時候咱們全部電腦都鏈接不上去了,其餘人就工做不了了,引用計數其實就像一個計數開關,只有當沒有電腦鏈接的時候,計數爲0,才容許執行shutdown命令(很是理想狀態下,現實中可不要這樣作)。面試
不一樣於java的GC回收機制,java中當沒有對象引用指向原先分配給某個對象的內存時,該內存便成爲垃圾。JVM的一個系統級線程會自動釋放該內存塊,除了釋放沒用的對象,垃圾回收也能夠清除內存記錄碎片。因爲建立對象和垃圾回收器釋放丟棄對象所佔的內存空間,內存會出現碎片。碎片是分配給對象的內存塊之間的空閒內存洞。碎片整理將所佔用的堆內存移到堆的一端,JVM將整理出的內存分配給新的對象。OC中這種特有的retain count機制,給咱們更多權限,讓開發者去控制對象釋放的時間以及如何去釋放,因此咱們得更加當心,過早的釋放內存,可能會引發程序崩潰,長時間不釋放佔用的內存,程序在運行一段時間後可能會發生內存泄露。數組
Objective-C採用了引用計數(retain count)。對象的內部保存一個數字,表示被引用的次數。例如,某個對象被兩個指針所指向(引用)那麼它的retain count爲2。須要銷燬對象的時候,不直接調用dealloc,而是調用release。release會讓retain count減1,只有retain count等於0,系統纔會調用dealloc真正銷燬這個對象。xcode
ClassA *obj1 = [[ClassA alloc] init];//對象生成時,retain count = 1 [obj1 release]; //release使retain count減1,retain count = 0,dealloc自動被調用,對象被銷燬
下面觀看一個例子服務器
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1 ClassA *obj2 = obj1; //retain count = 1 [obj1 hello]; //輸出hello [obj1 release]; //retain count = 0,對象被銷燬 [obj2 hello]; [obj2 release];
obj2引用了obj1,此時retain count爲1,當obj1執行完消息釋放後,retain count=0,此時obj2變成了無效指針,這裏再執行[obj2 release]會引發內存的過分釋放iphone
因此必定要謹記,不是alloc建立,而是指針賦值的時候,必定要retain,拿到對象的全部權oop
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1 ClassA *obj2 = obj1; //retain count = 1 [obj2 retain]; //retain count = 2 [obj1 hello]; //輸出hello [obj1 release]; //retain count = 2 – 1 = 1 [obj2 hello]; //輸出hello [obj2 release]; //retain count = 0,對象被銷燬
這樣寫的確能夠解決問題,可是若是對象很是多得時候,這樣的操做會不會太繁瑣了點,有沒有簡單一點的解決辦法?因此oc引入了自動釋放池autorelease pool,這也不一樣於java的全自動垃圾回收spa
新生成的對象調用autorelease就能夠了線程
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但無需調用release
若是存在指針賦值,與上面的代碼也類似
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 ClassA *obj2 = obj1; //retain count = 1 [obj2 retain]; //retain count = 2 [obj1 hello]; //輸出hello //對於obj1,無需調用(實際上不能調用)release [obj2 hello]; //輸出hello [obj2 release]; //retain count = 2-1 = 1
這裏有個有趣的問題,retain count不是1麼,還不能銷燬呀,何時才能銷燬呢?
因此咱們得了解一下autorelease pool的原理機制。
1)autorelease pool不是天生的,須要手動創立。只不過在新建一個iphone項目時,xcode會自動幫你寫好。autorelease pool的真名是NSAutoreleasePool。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2)NSAutoreleasePool內部包含一個數組(NSMutableArray),用來保存聲明爲autorelease的全部對象。若是一個對象聲明爲autorelease,系統所作的工做就是把這個對象加入到這個數組中去。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此對象加入autorelease pool中
3) NSAutoreleasePool自身在銷燬的時候,會遍歷一遍這個數組,release數組中的每一個成員。若是此時數組中成員的retain count爲1,那麼release以後,retain count爲0,對象正式被銷燬。若是此時數組中成員的retain count大於1,那麼release以後,retain count大於0,此對象依然沒有被銷燬,內存泄露。
那是否是有了自動釋放池autorelease pool就萬無一失了,其實否則
默認只有一個自動釋放池
int main (int argc, const char *argv[]) { NSAutoreleasePool *pool; pool = [[NSAutoreleasePool alloc] init]; // do something [pool release]; return (0); } // main
全部標記爲autorelease的對象在這個pool內被銷燬,可是若是這個自動釋放池裏面含有大量autorelease的對象,仍是容易形成內存不足的狀況,好比說:
int main (int argc, const char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int i, j; for (i = 0; i < 100; i++ ) { for (j = 0; j < 100000; j++ ) [NSString stringWithFormat:@"1234567890"];//產生的對象是autorelease的。 } [pool release]; return (0); } // main
這種狀況,大量內存被佔用,只有poll銷燬的時候,那些聲明爲autorelease對象才被銷燬,這對於ios程序來講並不樂觀,iphone內存自己有限,那有沒有更好的解決辦法,因此咱們能夠用autorelease嵌套機制來控制。
Objective-C程序中能夠嵌套建立多個autorelease pool。在須要大量建立局部變量的時候,能夠建立內嵌的autorelease pool來及時釋放內存。
int main (int argc, const char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int i, j; for (i = 0; i < 100; i++ ) { NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; for (j = 0; j < 100000; j++ ) [NSString stringWithFormat:@"1234567890"];//產生的對象是autorelease的。 [loopPool release]; } [pool release]; return (0); } // main