程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 我理解的objective-C內存管理

我理解的objective-C內存管理

編輯:關於C語言

我理解的objective-C內存管理


我開始學習iOS的時候,已經有ARC這個東西了,所以一開始就是在ARC的環境下學習,雖然對於內存管理有了解,但並沒認真去處理這方面的問題。工作中的項目是以前開始開發,使用的是非ARC,而且項目已經初步成型,不好改成ARC,所以我又回頭去研究內存管理。開始時很暈,不知那些地方改retain、那些地方該release,不過後來清楚了 許多,而且感覺挺喜歡這些東西,感覺對於程序的運行對了一個角度的認識,覺得很不錯。以後新建項目,估計很少會不用ARC,只是覺得內存管理還是挺有意思的,而且是自己用心研究了的,就總結下。

1、結構:

從對象的角度來看整個程序,是一個嵌套的結構,首先是一個A,這個A的構建運行過程中又會用到許多其他的對象,而其他的對象再又會用到一些其他的對象,其實很像json的結構。為什麼要先這樣去看程序呢?這樣可以把整個程序看成是一個對象包含了許多其他對象的結構,這樣內存管理就可以分解為兩部分:(1)需要釋放的對象釋放掉 (2)一個對象釋放的時候,它所用到得對象都改被釋放(除了某些還被其他對象使用的對象)

舉例說就是push到一個ViewController,然後再從那個ViewController返回,那麼這個viewController會釋放,這一般都不會出錯,但是這個Viewcontroller的成員變量都釋放了嗎?這個ViewController方法中構建的臨時對象都釋放了嗎?然後你可能在這個viewContrller裡面使用了一個自定義的對象,假設為pruductCell,那麼當你把改釋放這個對象的地方都處理好了,就可以再去看看它的.m文件,是否它使用的對象也釋放正確了。

我覺得從這樣一個結構去檢查挺好的。

2、一個對象釋放的時候,它所用到得對象都改被釋放(除了某些還被其他對象使用的對象):

首先,對象的引用和釋放要遵循:你retain的你要release,不是你retain的不要release(假設為准則1)

一個對象(假設為A)使用到的其他對象,可以分為兩類:一個是臨時對象(假設為B),用完就可以釋放;還一個是對象的屬性、成員變量(假設為C),這個一般都會在對象A各個方法中被重復用到,這樣就可以等到對象A被釋放的時候再釋放,也就是在A的dealloc方法裡面釋放。

臨時對象:

這個很好處理,用完了就釋放掉:

UIView * subView = [[UIView alloc]initWithFrame:CGRectMake(30, 100, 150, 260)];
    [self.view addSubview:subView]; //addsubView會retain子視圖
    [subView release];
這裡說的釋放不是值釋放內存,是指釋放掉對這個臨時對象的擁有權,就是執行release。對於擁有權,我的理解是對象A對某個對象(假設為B)進行了retain(包括alloc、copy等),這樣其它引用了這個對象的對象(假設為C),只要遵循准則1,當對象C release了對象B之後,對象B又會回到之前的引用計數。這樣只要對象A不release對象B,B的內存就永遠不會釋放。這就像對象A是擁有了對象B。

而對臨時對象release,這樣當引用它的對象也release它之後,它就沒有被任何對象擁有,內存就會被釋放,這樣剛好。例如這裡,當subView被從self.view上面移除後,基本就是不需要用這個subView的時候,這時它內存被釋放,剛好。

因為臨時變量構建在方法裡,出了這個方法就這個指針就沒了,就沒法釋放對它的所有權了,所以必須在方法結束前release。

屬性、成員變量:

為了在對象(A)存在的整個過程中,它的屬性、成員變量都活著,必須不能release,否則可能會引用計數為0,從而導致下次用的時候它內存已經被釋放了,從而程序崩潰:

_subView = [[UIView alloc]initWithFrame:CGRectMake(30, 100, 150, 260)];
    [self.view addSubview:_subView];
    [_subView release];
    
    [_subView removeFromSuperview]; 
    
    NSLog(@"%@",_subView); //程序崩潰
不是說成員變量就不釋放,而是釋放了,下次再用就沒有了。而既然是成員變量、屬性,那麼就是會再多個方法裡面用到的,甚至會被其他的類的對象使用,所以要保持它是“活的”。


3、關於屬性定義時的關鍵詞retain、assign:

使用屬性定義變量時,自帶set\get方法,如果在變量賦值的時候使用self.XXX = YYY來方式賦值,其實是調用了set方法,而與內存管理有關的問題是:如果屬性定義時使用retain,set方法是:

-(void )setXXX:(UIView *)XXX{
    [XXX retain];
    [_XXX release];
    _XXX = XXX;
}
對新值會retain,對舊值會release,從而使得對傳入的YYY獲取擁有權,保證在self使用它期間,它的內存永不會被釋放掉。這也遵循准則1。而使用assign卻是簡單賦值,沒有retain\release的操作。

如果屬性是assign或者干脆沒有使用self.XXX進行賦值,那麼就不會retain賦值對象,那麼:

UIView * view1 = [[UIView alloc]initWithFrame:CGRectMake(30, 100, 150, 260)];
    _XXX = view1;
    [view1 release];
這裡不會崩潰,但是接下來_XXX就變成了僵屍指針了,它指向位置的內存被釋放了,這也是assgin脆弱的地方,所以需要較長時間對某個對象的使用,最好使用retain,保證引用計數不會為0。

4、關於autoRelease:

首先類方法構建對象,返回值一般自帶autoRelease,使用時注意下。

為什麼要使用autoRelease,我的理解是延緩release。因為我構建的,所以我要釋放,但是如果我release了,這個對象內存就被釋放了,那不就白構建了嗎?可以構建,然後把它給需要使用的地方,然後再釋放。比如帶放回值得方法:

-(NSString *)createString{
    NSString * str = [[NSString alloc]init];
    return str;
}
如果我在方法裡面不釋放,然後外面使用的時候遵循准則1,那麼最後這個str的引用計數會保持在1,不會被釋放,這樣很成問題。但是如果釋放了,那return的就是空值,沒意義了。autoRelease使用了,就可以return一個有意義的值,然後最後這個對象在自動釋放池裡能被釋放,不用擔心內存洩露。

5、特殊的方法和對象:

addSubview會對添加進來的視圖retain,NSMutableArray的adObject也會retain添加的對象,block會對裡面使用到的變量全部retain,很可能造成循環引用,NStimer在沒有釋放的時候,會retain構建是指定的target,想要釋放掉target,就需要先停止NSTimer。

字符串常量不需要我們管理內存。


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved