我理解的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。

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章