自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Objective-C內(nèi)存管理教程和原理剖析

移動(dòng)開發(fā) iOS
本文介紹的是Objective-C內(nèi)存管理教程和原理剖析,還詳細(xì)的對(duì)Autorelease進(jìn)行了介紹,我們來看內(nèi)容。

Objective-C內(nèi)存管理教程和原理剖析是本文要介紹的內(nèi)容,在iPhoneMac 平臺(tái)運(yùn)行,不多說,我們來看內(nèi)容。

初學(xué)objectice-C的朋友都有一個(gè)困惑,總覺得對(duì)objective-C內(nèi)存管理機(jī)制琢磨不透,程序經(jīng)常內(nèi)存泄漏或莫名其妙的崩潰。我在這里總結(jié)了自己對(duì)objective-C內(nèi)存管理機(jī)制的研究成果和經(jīng)驗(yàn),寫了這么一個(gè)由淺入深的教程。希望對(duì)大家有所幫助,也歡迎大家一起探討。

此文涉及的內(nèi)存管理是針對(duì)于繼承于NSObject的Class。

一 基本原理

Objective-C內(nèi)存管理機(jī)制與.Net/Java那種全自動(dòng)的垃圾回收機(jī)制是不同的,它本質(zhì)上還是C語言中的手動(dòng)管理方式,只不過稍微加了一些自動(dòng)方法。

1、Objective-C的對(duì)象生成于堆之上,生成之后,需要一個(gè)指針來指向它。

  1. ClassA *obj1 = [[ClassA alloc] init]; 

2、Objective-C的對(duì)象在使用完成之后不會(huì)自動(dòng)銷毀,需要執(zhí)行dealloc來釋放空間(銷毀),否則內(nèi)存泄露。

  1. [obj1 dealloc];  

這帶來了一個(gè)問題。下面代碼中obj2是否需要調(diào)用dealloc?

  1. ClassA *obj1 = [[ClassA alloc] init];  
  2. ClassA *obj2 = obj1;  
  3. [obj1 hello]; //輸出hello  
  4. [obj1 dealloc];  
  5. [obj2 hello]; //能夠執(zhí)行這一行和下一行嗎?  
  6. [obj2 dealloc];  

不能,因?yàn)閛bj1和obj2只是指針,它們指向同一個(gè)對(duì)象,[obj1 dealloc]已經(jīng)銷毀這個(gè)對(duì)象了,不能再調(diào)用[obj2 hello]和[obj2 dealloc]。obj2實(shí)際上是個(gè)無效指針。

如何避免無效指針?請(qǐng)看下一條。

3、Objective-C采用了引用計(jì)數(shù)(ref count或者retain count)。對(duì)象的內(nèi)部保存一個(gè)數(shù)字,表示被引用的次數(shù)。例如,某個(gè)對(duì)象被兩個(gè)指針?biāo)赶颍ㄒ茫┠敲此膔etain count為2。需要銷毀對(duì)象的時(shí)候,不直接調(diào)用dealloc,而是調(diào)用release。release會(huì)讓retain count減1,只有retain count等于0,系統(tǒng)才會(huì)調(diào)用dealloc真正銷毀這個(gè)對(duì)象。

  1. ClassA *obj1 = [[ClassA alloc] init]; //對(duì)象生成時(shí),retain count = 1 
  2. [obj1 release]; //release使retain count減1,retain count = 0,dealloc自動(dòng)被調(diào)用,對(duì)象被銷毀 

我們回頭看看剛剛那個(gè)無效指針的問題,把dealloc改成release解決了嗎?

  1. ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1 
  2. ClassA *obj2 = obj1; //retain count = 1 
  3. [obj1 hello]; //輸出hello  
  4. [obj1 release]; //retain count = 0,對(duì)象被銷毀  
  5. [obj2 hello];  
  6. [obj2 release];   
  7. [obj1 release]之后,obj2依然是個(gè)無效指針。問題依然沒有解決。解決方法見下一條。 

4、Objective-C指針賦值時(shí),retain count不會(huì)自動(dòng)增加,需要手動(dòng)retain。

  1. ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1 
  2. ClassA *obj2 = obj1; //retain count = 1 
  3. [obj2 retain]; //retain count = 2 
  4. [obj1 hello]; //輸出hello  
  5. [obj1 release]; //retain count = 2 – 11 = 1  
  6. [obj2 hello]; //輸出hello  
  7. [obj2 release]; //retain count = 0,對(duì)象被銷毀 

問題解決!注意,如果沒有調(diào)用[obj2 release],這個(gè)對(duì)象的retain count始終為1,不會(huì)被銷毀,內(nèi)存泄露。(1-4可以參考附件中的示例程序memman-no-pool.m)

這樣的確不會(huì)內(nèi)存泄露,但似乎有點(diǎn)麻煩,有沒有簡單點(diǎn)的方法?見下一條。

5、Objective-C中引入了autorelease pool(自動(dòng)釋放對(duì)象池),在遵守一些規(guī)則的情況下,可以自動(dòng)釋放對(duì)象。(autorelease pool依然不是.Net/Java那種全自動(dòng)的垃圾回收機(jī)制)

(1)新生成的對(duì)象,只要調(diào)用autorelease就行了,無需再調(diào)用release!

  1. ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但無需調(diào)用release 

(2)對(duì)于存在指針賦值的情況,代碼與前面類似。

  1. ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 
  2. ClassA *obj2 = obj1; //retain count = 1 
  3. [obj2 retain]; //retain count = 2 
  4. [obj1 hello]; //輸出hello  
  5. //對(duì)于obj1,無需調(diào)用(實(shí)際上不能調(diào)用)release  
  6. [obj2 hello]; //輸出hello  
  7. [obj2 release]; //retain count = 2-1 = 1 

細(xì)心的讀者肯定能發(fā)現(xiàn)這個(gè)對(duì)象沒有被銷毀,何時(shí)銷毀呢?誰去銷毀它?(可以參考附件中的示例程序memman-with-pool.m)請(qǐng)看下一條。

6、autorelease pool原理剖析。(其實(shí)很簡單的,一定要堅(jiān)持看下去,否則還是不能理解Objective-C的內(nèi)存管理機(jī)制。)

(1)autorelease pool不是天生的,需要手動(dòng)創(chuàng)立。只不過在新建一個(gè)iphone項(xiàng)目時(shí),xcode會(huì)自動(dòng)幫你寫好。autorelease pool的真名是NSAutoreleasePool。

  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

(2)NSAutoreleasePool內(nèi)部包含一個(gè)數(shù)組(NSMutableArray),用來保存聲明為autorelease的所有對(duì)象。如果一個(gè)對(duì)象聲明為autorelease,系統(tǒng)所做的工作就是把這個(gè)對(duì)象加入到這個(gè)數(shù)組中去。

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此對(duì)象加入autorelease pool中

(3)NSAutoreleasePool自身在銷毀的時(shí)候,會(huì)遍歷一遍這個(gè)數(shù)組,release數(shù)組中的每個(gè)成員。如果此時(shí)數(shù)組中成員的retain count為1,那么release之后,retain count為0,對(duì)象正式被銷毀。如果此時(shí)數(shù)組中成員的retain count大于1,那么release之后,retain count大于0,此對(duì)象依然沒有被銷毀,內(nèi)存泄露。

(4)默認(rèn)只有一個(gè)autorelease pool,通常類似于下面這個(gè)例子。

  1. int main (int argc, const char *argv[])  
  2. {  
  3. NSAutoreleasePool *pool;  
  4. pool = [[NSAutoreleasePool alloc] init];  
  5. // do something  
  6. [pool release];  
  7. return (0);  
  8. } // main 

所有標(biāo)記為autorelease的對(duì)象都只有在這個(gè)pool銷毀時(shí)才被銷毀。如果你有大量的對(duì)象標(biāo)記為autorelease,這顯然不能很好的利用內(nèi)存,在iphone這種內(nèi)存受限的程序中是很容易造成內(nèi)存不足的。例如:

  1. int main (int argc, const char *argv[])  
  2. {  
  3. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  4. int i, j;  
  5. for (i = 0; i < 100; i++ )  
  6. {  
  7.  for (j = 0; j < 100000; j++ )  
  8.     [NSString stringWithFormat:@"1234567890"];//產(chǎn)生的對(duì)象是autorelease的。  
  9. }  
  10. [pool release];  
  11. return (0);  
  12. } // main 

(可以參考附件中的示例程序memman-many-objs-one-pool.m,運(yùn)行時(shí)通過監(jiān)控工具可以發(fā)現(xiàn)使用的內(nèi)存在急劇增加,直到pool銷毀時(shí)才被釋放)你需要考慮下一條。
 
7、Objective-C程序中可以嵌套創(chuàng)建多個(gè)autorelease pool。在需要大量創(chuàng)建局部變量的時(shí)候,可以創(chuàng)建內(nèi)嵌的autorelease pool來及時(shí)釋放內(nèi)存。(感謝網(wǎng)友hhyytt和neogui的提醒,某些情況下,系統(tǒng)會(huì)自動(dòng)創(chuàng)建autorelease pool, 請(qǐng)參見第四章)

  1. int main (int argc, const char *argv[])  
  2. {  
  3. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  4. int i, j;  
  5. for (i = 0; i < 100; i++ )  
  6. {  
  7.  NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];  
  8.  for (j = 0; j < 100000; j++ )  
  9.     [NSString stringWithFormat:@"1234567890"];//產(chǎn)生的對(duì)象是autorelease的。  
  10.  [loopPool release];  
  11. }  
  12. [pool release];  
  13. return (0);  
  14. } // main 

(可以參考附件中的示例程序memman-many-objs-many-pools.m,占用內(nèi)存的變化極?。?/p>

#p#

二 口訣與范式

1、口訣。

(1)誰創(chuàng)建,誰釋放(類似于“誰污染,誰治理”)。如果你通過alloc、new或copy來創(chuàng)建一個(gè)對(duì)象,那么你必須調(diào)用release或autorelease。換句話說,不是你創(chuàng)建的,就不用你去釋放。
例如,你在一個(gè)函數(shù)中alloc生成了一個(gè)對(duì)象,且這個(gè)對(duì)象只在這個(gè)函數(shù)中被使用,那么你必須在這個(gè)函數(shù)中調(diào)用release或autorelease。如果你在一個(gè)class的某個(gè)方法中alloc一個(gè)成員對(duì)象,且沒有調(diào)用autorelease,那么你需要在這個(gè)類的dealloc方法中調(diào)用release;如果調(diào)用了autorelease,那么在dealloc方法中什么都不需要做。

(2)除了alloc、new或copy之外的方法創(chuàng)建的對(duì)象都被聲明了autorelease

(3)誰retain,誰release。只要你調(diào)用了retain,無論這個(gè)對(duì)象是如何生成的,你都要調(diào)用release。有時(shí)候你的代碼中明明沒有retain,可是系統(tǒng)會(huì)在默認(rèn)實(shí)現(xiàn)中加入retain。不知道為什么蘋果公司的文檔沒有強(qiáng)調(diào)這個(gè)非常重要的一點(diǎn),請(qǐng)參考范式2.7和第三章。

2、范式。

范式就是模板,就是依葫蘆畫瓢。由于不同人有不同的理解和習(xí)慣,我總結(jié)的范式不一定適合所有人,但我能保證照著這樣做不會(huì)出問題。

(1)創(chuàng)建一個(gè)對(duì)象。

  1. ClassA *obj1 = [[ClassA alloc] init]; 

(2)創(chuàng)建一個(gè)autorelease的對(duì)象。

  1. ClassA *obj1 = [[[ClassA alloc] init] autorelease]; 

(3)Release一個(gè)對(duì)象后,立即把指針清空。(順便說一句,release一個(gè)空指針是合法的,但不會(huì)發(fā)生任何事情)

  1. [obj1 release];  
  2. obj1 = nil

(4)指針賦值給另一個(gè)指針。

  1. ClassA *obj2 = obj1;  
  2. [obj2 retain];  
  3. //do something  
  4. [obj2 release];  
  5. obj2 = nil

(5)在一個(gè)函數(shù)中創(chuàng)建并返回對(duì)象,需要把這個(gè)對(duì)象設(shè)置為autorelease

  1. ClassA *Func1()  
  2. {  
  3.   ClassA *obj = [[[ClassA alloc]init]autorelease];  
  4.   return obj;  

(6)在子類的dealloc方法中調(diào)用基類的dealloc方法

  1. -(void) dealloc  
  2. {         [super dealloc];  

(7)在一個(gè)class中創(chuàng)建和使用property。

聲明一個(gè)成員變量。

  1. ClassB *objB; 

聲明property,加上retain參數(shù)。

  1. @property (retain) ClassB* objB; 

定義property。(property的默認(rèn)實(shí)現(xiàn)請(qǐng)看第三章)

  1. @synthesize objB; 

除了dealloc方法以外,始終用.操作符的方式來調(diào)用property。

self.objB 或者objA.objB

在dealloc方法中release這個(gè)成員變量。

  1. [objB release]; 

示例代碼如下(詳細(xì)代碼請(qǐng)參考附件中的memman-property.m,你需要特別留意對(duì)象是在何時(shí)被銷毀的。):

  1. @interface ClassA : NSObject  
  2. {  
  3.          ClassB* objB;  
  4. }  
  5.  
  6. @property (retain) ClassB* objB;  
  7. @end  
  8. @implementation ClassA  
  9. @synthesize objB;  
  10. -(void) dealloc  
  11. {  
  12.          [objB release];  
  13.          [super dealloc];  
  14. }  
  15. @end 

給這個(gè)property賦值時(shí),有手動(dòng)release和autorelease兩種方式。

  1. void funcNoAutorelease()  
  2. {  
  3.          ClassB *objB1 = [[ClassB alloc]init];  
  4.          ClassA *objA = [[ClassA alloc]init];  
  5.          objA.objB = objB1;  
  6.          [objB1 release];  
  7.          [objA release];  
  8. }  
  9.  
  10. void funcAutorelease()  
  11. {  
  12.          ClassB *objB1 = [[[ClassB alloc]init] autorelease];  
  13.          ClassA *objA = [[[ClassA alloc]init] autorelease];  
  14.          objA.objB = objB1;  

#p#

三 @property (retain)和@synthesize的默認(rèn)實(shí)現(xiàn)

在這里解釋一下@property (retain) ClassB* objB;和@synthesize objB;背后到底發(fā)生了什么(retain property的默認(rèn)實(shí)現(xiàn))。property實(shí)際上是getter和setter,針對(duì)有retain參數(shù)的property,背后的實(shí)現(xiàn)如下(請(qǐng)參考附件中的memman-getter-setter.m,你會(huì)發(fā)現(xiàn),結(jié)果和memman-property.m一樣):

  1. @interface ClassA : NSObject  
  2. {  
  3.          ClassB *objB;  
  4. }  
  5. -(ClassB *) getObjB;  
  6.  
  7. -(void) setObjB:(ClassB *) value;  
  8. @end  
  9. @implementation ClassA  
  10.  
  11. -(ClassB*) getObjB  
  12. {  
  13.          return objB;  
  14. }  
  15.  
  16. -(void) setObjB:(ClassB*) value  
  17. {  
  18.          if (objB != value)  
  19.          {  
  20.                    [objB release];  
  21.                    objB = [value retain];  
  22.          }  

在setObjB中,如果新設(shè)定的值和原值不同的話,必須要把原值對(duì)象release一次,這樣才能保證retain count是正確的。

由于我們?cè)赾lass內(nèi)部retain了一次(雖然是默認(rèn)實(shí)現(xiàn)的),所以我們要在dealloc方法中release這個(gè)成員變量。

  1. -(void) dealloc  
  2. {  
  3.          [objB release];  
  4.          [super dealloc];  

四 系統(tǒng)自動(dòng)創(chuàng)建新的autorelease pool

在生成新的Run Loop的時(shí)候,系統(tǒng)會(huì)自動(dòng)創(chuàng)建新的autorelease pool(非常感謝網(wǎng)友hhyytt和neogui的提醒)。注意,此處不同于xcode在新建項(xiàng)目時(shí)自動(dòng)生成的代碼中加入的autorelease pool,xcode生成的代碼可以被刪除,但系統(tǒng)自動(dòng)創(chuàng)建的新的autorelease pool是無法刪除的(對(duì)于無Garbage Collection的環(huán)境來說)。Objective-C沒有給出實(shí)現(xiàn)代碼,官方文檔也沒有說明,但我們可以通過小程序來證明。

在這個(gè)小程序中,我們先生成了一個(gè)autorelease pool,然后生成一個(gè)autorelease的ClassA的實(shí)例,再在一個(gè)新的run loop中生成一個(gè)autorelease的ClassB的對(duì)象(注意,我們并沒有手動(dòng)在新run loop中生成autorelease pool)。精簡的示例代碼如下,詳細(xì)代碼請(qǐng)見附件中的memman-run-loop-with-pool.m。

  1. int main(int argc, char**argv)    
  2. {  
  3.          NSLog(@"create an autorelasePool\n");  
  4.          NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];      
  5.          NSLog(@"create an instance of ClassA and autorelease\n");  
  6.          ClassA *obj1 = [[[ClassA alloc] init] autorelease];  
  7.          NSDate *now = [[NSDate alloc] init];  
  8.          NSTimer *timer = [[NSTimer alloc] initWithFireDate:now  
  9.                    interval:0.0  
  10.                    target:obj1  
  11.                    selector:@selector(createClassB)  
  12.                    userInfo:nil  
  13.                    repeats:NO];   
  14.          NSRunLoop *runLoop = [NSRunLoop currentRunLoop];  
  15.          [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];  
  16.          [timer release];  
  17.          [now release];  
  18.          [runLoop run]; //在新loop中調(diào)用一函數(shù),生成ClassB的autorelease實(shí)例  
  19.          NSLog(@"releasing autorelasePool\n");   
  20.          [pool release];   
  21.          NSLog(@"autorelasePool is released\n");   
  22.          return 0;  
  23. }   

輸出如下:

  1. create an autorelasePool  
  2. create an instance of ClassA and autorelease  
  3. create an instance of ClassB and autorelease  
  4. ClassB destroyed  
  5. releasing autorelasePool  
  6. ClassA destroyed  
  7. autorelasePool is released 

注意在我們銷毀autorelease pool之前,ClassB的autorelease實(shí)例就已經(jīng)被銷毀了。

有人可能會(huì)說,這并不能說明新的run loop自動(dòng)生成了一個(gè)新的autorelease pool,說不定還只是用了老的autorelease pool,只不過后來drain了一次而已。我們可以在main函數(shù)中不生成autorelease pool。精簡的示例代碼如下,詳細(xì)代碼請(qǐng)見附件中的memman-run-loop-without-pool.m。

  1. int main(int argc, char**argv)    
  2. {  
  3.          NSLog(@"No autorelasePool created\n");  
  4.          NSLog(@"create an instance of ClassA\n");  
  5.          ClassA *obj1 = [[ClassA alloc] init];  
  6.          NSDate *now = [[NSDate alloc] init];  
  7.          NSTimer *timer = [[NSTimer alloc] initWithFireDate:now  
  8.                    interval:0.0  
  9.                    target:obj1  
  10.                    selector:@selector(createClassB)  
  11.                    userInfo:nil  
  12.                    repeats:NO];   
  13.          NSRunLoop *runLoop = [NSRunLoop currentRunLoop];  
  14.          [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];  
  15.          [timer release];  
  16.          [now release];  
  17.          [runLoop run]; //在新loop中調(diào)用一函數(shù),生成ClassB的autorelease實(shí)例  
  18.          NSLog(@"Manually release the instance of ClassA\n");  
  19.          [obj1 release];  
  20.          return 0;  
  21. }   

輸出如下:

  1. No autorelasePool created  
  2. create an instance of ClassA  
  3. create an instance of ClassB and autorelease  
  4. ClassB destroyed  
  5. Manually release the instance of ClassA  
  6. ClassA destroyed 

我們可以看出來,我們并沒有創(chuàng)建任何autorelease pool,可是ClassB的實(shí)例依然被自動(dòng)銷毀了,這說明新的run loop自動(dòng)創(chuàng)建了一個(gè)autorelease pool,這個(gè)pool在新的run loop結(jié)束的時(shí)候會(huì)銷毀自己(并自動(dòng)release所包含的對(duì)象)。

補(bǔ)充說明

在研究retain count的時(shí)候,我不建議用NSString。因?yàn)樵谙旅娴恼Z句中,

  1. NSString *str1 = @”constant string”; 

str1的retain count是個(gè)很大的數(shù)字。Objective-C對(duì)常量字符串做了特殊處理。

當(dāng)然,如果你這樣創(chuàng)建NSString,得到的retain count依然為1

  1. NSString *str2 = [NSString stringWithFormat:@”123”]; 

涉及的示例程序代碼(已去除隱藏,覺得有用的話請(qǐng)頂一下此文):http://files.cnblogs.com/VinceYuan/objective-c-memman.zip

小結(jié):Objective-C內(nèi)存管理教程和原理剖析的內(nèi)容介紹完了,希望本文對(duì)你有所幫助

轉(zhuǎn)載時(shí)必須包含原始鏈接http://vinceyuan.cnblogs.com/,且必須包含此版權(quán)聲明的完整內(nèi)容。

責(zé)任編輯:zhaolei 來源: CSDN博客
相關(guān)推薦

2011-07-19 15:15:09

Objective-C 內(nèi)存

2013-04-11 14:32:00

Objective-CiOS開發(fā)內(nèi)存管理@synthesize

2013-04-11 14:37:36

Objective-CiOS內(nèi)存管理系統(tǒng)自動(dòng)創(chuàng)建新的aut

2013-04-11 13:57:27

Objective-CiOS開發(fā)內(nèi)存管理

2013-04-11 14:16:57

Objective-CiOS開發(fā)內(nèi)存管理

2011-07-18 17:14:16

Objective-C 內(nèi)存 Cocoa

2011-07-29 16:08:31

Objective-C 內(nèi)存

2011-07-27 17:10:30

Objective-C 持久化

2011-05-11 15:45:50

內(nèi)存管理Objective-C

2011-07-20 17:04:43

Objective-C 內(nèi)存 內(nèi)存泄露

2011-07-21 10:10:42

Objective-C 內(nèi)存 Autoreleas

2011-07-21 09:32:07

Objective-C 內(nèi)存 Autoreleas

2011-08-18 13:28:35

Objective-C內(nèi)存

2011-08-01 11:37:41

iPhone Objective- 內(nèi)存

2011-08-16 17:43:47

Objective-C內(nèi)存管理Autorelease

2011-07-08 13:49:46

Objective-C UUID

2011-08-05 14:03:39

Objective-C 對(duì)象 模板

2011-08-16 10:23:04

Objective-CNSAutoreleaXcode常用鍵

2011-08-22 09:48:16

WindowsObjective-C

2013-05-02 10:51:17

iOS開發(fā)Objective-C@property
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)