iOS面試_1.淺析內存管理

  爲了開學的面試,就在博客裏總結一下面試會問到的問題,今天就來談談內存管理,看到一篇文章很是不錯,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 count2。須要銷燬對象的時候,不直接調用dealloc,而是調用releaserelease會讓retain count1,只有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 count1,那麼release以後,retain count0,對象正式被銷燬。若是此時數組中成員的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
相關文章
相關標籤/搜索