ios開發(fā)內(nèi)存管理及內(nèi)存泄漏整理總結(jié)

一、1、IOS開發(fā)中,內(nèi)存中的對象主要有兩類

一類是值類型,比如int、float、struct等基本數(shù)據(jù)類型。

一類是引用類型,即繼承自NSObject類的所有的OC對象。

A、 值類型會被放入棧中,

B、 引用類型會被放到堆中

@ 全局/靜態(tài)存儲區(qū),全局變量和靜態(tài)變量的存儲區(qū)域

@棧區(qū),在函數(shù)執(zhí)行過程中,函數(shù)內(nèi)局部變量的存儲單元可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束后這些存儲單元自動被釋放。

@堆區(qū),亦稱動態(tài)分配區(qū),由程序在運(yùn)行過程中動態(tài)申請分配和管理的區(qū),通常說的內(nèi)存管理,基本是指對于這一內(nèi)存區(qū)塊的管理

@常量區(qū),存儲程序運(yùn)行過程中用到的各種常量,不允許修改

二、Objective-C管理內(nèi)存的方式

每個對象(特指:類的實例)內(nèi)部都有一個retainCount的引用計數(shù),

使用alloc, new, copy或者mutableCopy等以及調(diào)用addObject等方法時,引用計數(shù)器+1,使用release時,引用計數(shù)器-1,當(dāng)引用計數(shù)器為0時,對象被釋放

  1. iOS內(nèi)存管理的法則(Swift不適用,Swift自動管理內(nèi)存)

? 誰創(chuàng)建誰釋放

? 誰retain誰釋放

2)MRC手動管理內(nèi)存(人工引用計數(shù))

當(dāng)引用計數(shù)為0的時候,必須回收,引用計數(shù)不為0,不能回收,如果引用計數(shù)為0,但是沒有回收,會造成內(nèi)存泄露。如果引用計數(shù)為0,繼續(xù)釋放,會造成野指針。為了避免出現(xiàn)野指針,我們在釋放的時候,會先讓指針=nil。

3)ARC自動管理內(nèi)存(自動引用計數(shù))

在ARC模式下,只要沒有強(qiáng)指針(強(qiáng)引用)指向?qū)ο?,對象就會被釋放。在ARC模式下,不允許使用retain、release、retainCount等方法。并且,如果使用dealloc方法時,不允許調(diào)用[superdealloc]方法。

ARC模式下的property變量修飾詞為strong、weak,相當(dāng)于MRC模式下的retain、assign。strong:代替retain,缺省關(guān)鍵詞,代表強(qiáng)引用。weak:代替assign,聲明了一個可以自動設(shè)置nil的弱引用,但是比assign多一個功能,指針指向的地址被釋放之后,指針本身也會自動被釋放。

三、與內(nèi)存有關(guān)的修飾符

@property的參數(shù)分為三類,也就是說參數(shù)最多可以有三個,中間用逗號分隔,每類參數(shù)可以從上表三類參數(shù)中人選一個。如果不進(jìn)行設(shè)置或者只設(shè)置其中一類參數(shù),程序會使用三類中的各個默認(rèn)參數(shù),默認(rèn)參數(shù):(atomic,readwrite,assign)

一般情況下如果在多線程開發(fā)中一個屬性可能會被兩個及兩個以上的線程同時訪問,此時可以考慮atomic屬性,否則建議使用nonatomic,不加鎖,效率較高;

retain,相當(dāng)于ARC中的strong

assign,相當(dāng)于ARC中的weak

屬性參數(shù)

ARC下基本數(shù)據(jù)類型默認(rèn)的屬性參數(shù)為(atomic,readwrite,assign),對象類型默認(rèn)的屬性參數(shù)為(atomic,readwrite,strong)

非ARC下基本數(shù)據(jù)類型默認(rèn)的屬性參數(shù)為(atomic,readwrite,assign),對象的默認(rèn)屬性參數(shù)為(atomic,readwrite,retain)

ARC下

assgin : 基本數(shù)據(jù)類型、枚舉、結(jié)構(gòu)體(非OC對象)

strong : 除NSString\block以外的OC對象

copy : NSString,數(shù)組,字典,block

weak : 當(dāng)2個對象相互引用,一端用strong,一端用weak。引用記數(shù)不會加1

非ARC下

assign,用于基本數(shù)據(jù)類型和C數(shù)據(jù)類型(int, float, double, char)另外還有id

retain,通常用于非字符串對象

copy,通常用于字符串對象、block、NSArray、NSDictionary

@@ strong與copy都是強(qiáng)類型,但這兩者有什么區(qū)別呢。什么情況下使用copy,什么時候使用strong呢?

copy是深復(fù)制,會指向新的對象,即創(chuàng)建新的內(nèi)存地址。不會跟著賦值的對象的值變化而變化,即不可變。 strong則是指向賦值對象所在的地址,所以當(dāng)賦值對象值發(fā)生變化時,也會跟著改變。

使用copy的概念應(yīng)該是出于安全的考慮。防止賦值給它的是可變的數(shù)據(jù)。

所以什么時候使用 strong,賦值對象改變時也要跟著改變。使用copy,賦值對象改變時property不跟著變化。

@@、retain、copy、assign的區(qū)別:

1.retain:當(dāng)對一個對象A調(diào)用retain,然后賦值給B時,對象的引用計數(shù)加1,A和B指向同一個內(nèi)存地址。

2.copy:當(dāng)對一個對象A調(diào)用retain,然后賦值給B時,對象的引用計數(shù)加1,而且生成了一個新的拷貝,A和B指向不一樣的內(nèi)存地址。

3.assign:當(dāng)對一個對象A調(diào)用retain,然后賦值給B時,對象的引用計數(shù)不變,A和B指向同一個地址。

四、那些內(nèi)存管理中的坑

1)循環(huán)引用

即A持有了B,B持有了A,導(dǎo)致無論是先釋放A還是B

解決這種親密關(guān)系導(dǎo)致的循環(huán)引用,采用弱引用即可

  1. Block中的坑

Block在訪問對象變量(即類對象)時有兩條隱含的retain對象規(guī)則,即:

    A、如果訪問類屬性對象變量,則Block會強(qiáng)引用self,即retain一次類對象本身
  1.       B、如果訪問了局部對象變量,則Block會強(qiáng)引用局部變量自身一次
    

3)閉包中循環(huán)引用

  1.   閉包中變量的訪問范圍及持有變了隱含規(guī)則同Block
    

4)NSTimer中的對象retain問題

repeats參數(shù)被設(shè)置成YES時,target中的對象將永遠(yuǎn)不會被釋放,只有調(diào)用invalidate方法之后才會釋放target對象,從而釋放接收處理target對象。

  1. performSelector中的對象retain問題

只有當(dāng)執(zhí)行完成之后才會釋放target和argument對象,它的執(zhí)行前提條件是:1)時間到;2)滿足指定的Loop Modes。因此在發(fā)起該方法的類銷毀之前該方法不一定會被執(zhí)行,因此就會存在內(nèi)存泄漏的風(fēng)險。能否在dealloc或deinit中釋放呢?請看客考慮

五、自動釋放池

自動內(nèi)存釋放使用@autoreleasepool關(guān)鍵字聲明一個代碼塊,如果一個對象在初始化時調(diào)用了autorelase方法,那么當(dāng)代碼塊執(zhí)行完之后,在塊中調(diào)用過autorelease方法的對象都會自動調(diào)用一次release方法。這樣一來就起到了自動釋放的作用,同時對象的銷毀過程也得到了延遲(統(tǒng)一調(diào)用release方法)。對象的釋放延遲到自動釋放池銷毀的時候。不過這只是一種半自動的機(jī)制。

注意:

1)autorelease不會改變對象的引用記數(shù),如果Person對象引用記數(shù)非0,放入自動釋放池是無法被銷毀的。

2)自動釋放池實質(zhì)是當(dāng)自動釋放池銷毀后調(diào)用對象的release方法。

3)如果一個操作比較占用內(nèi)存(對象比較多或者對象占用資源比較多),最好不要放入自動釋放池或者考慮放入多個釋放池中。

4)ObjC中類庫中的靜態(tài)方法一般都不需要手動釋放,內(nèi)部已經(jīng)調(diào)用了autorelease方法.

1.自動釋放池實現(xiàn)了對象的延遲釋放,將釋放時機(jī)延后。當(dāng)對一個對象調(diào)用autorelease方法后,對象被加入自動釋放池。當(dāng)自動釋放池釋放時,會對自動釋放池中的對象調(diào)用release方法。

2.主線程會自動創(chuàng)建自動釋放池,自己創(chuàng)建的線程需要自己負(fù)責(zé)創(chuàng)建自動釋放池。在一個RunLoop周期開始時,系統(tǒng)會創(chuàng)建一個自動釋放池,當(dāng)RunLoop周期結(jié)束時,系統(tǒng)會釋放之前創(chuàng)建的自動釋放池。如果我們在使用autorelease時沒有自己創(chuàng)建自動釋放池,對象會在它所在的RunLoop周期結(jié)束時被釋放掉。一個UI事件,Timer調(diào)用,delegate調(diào)用,都會是一個新的Runloop。

3.類似于[NSString stringWithFormat:]這樣的類方法創(chuàng)建的對象默認(rèn)是使用了自動釋放池的,不需要釋放。

4.當(dāng)在短時間內(nèi)大量的使用自動釋放對象,要手動使用自動釋放池來釋放對象,否則內(nèi)存會在短時間內(nèi)瘋漲。

六、循環(huán)引用

  1. @class

對于循環(huán)依賴關(guān)系來說,比方A類引用B類,同時B類也引用A類

這種代碼編譯會報錯。當(dāng)使用@class在兩個類相互聲明,就不會出現(xiàn)編譯報錯

用法概括

使用@class 類名; 就可以引用一個類,說明一下它是一個類

和#import的區(qū)別(面試題)

l #import方式會包含被引用類的所有信息,包括被引用類的變量和方法;@class方式只是告訴編譯器在A.h文件中 B *b 只是類的聲明,具體這個類里有什么信息,這里不需要知道,等實現(xiàn)文件中真正要用到時,才會真正去查看B類中信息

l 如果有上百個頭文件都#import了同一個文件,或者這些文件依次被#improt,那么一旦最開始的頭文件稍有改動,后面引用到這個文件的所有類都需要重新編譯一遍,這樣的效率也是可想而知的,而相對來講,使用@class方式就不會出現(xiàn)這種問題了

l 在.m實現(xiàn)文件中,如果需要引用到被引用類的實體變量或者方法時,還需要使用#import方式引入被引用類

問題:如何解決循環(huán)引用的內(nèi)存管理。

解決方案:一端用assign,一端用retain

/*

1.@class的作用:僅僅告訴編譯器,某個名稱是一個類

2.開發(fā)中引用一個類的規(guī)范

1>在.h文件中用@class來聲明類

2>在.m文件中用#import來包含類的所有東西

  1. 循環(huán)retain

比如A對象retain了B對象,B對象retain了A對象

這樣會導(dǎo)致A對象和B對象永遠(yuǎn)無法釋放

七、didReceiveMemoryWarning、dealloc方法使用

1.didReceiveMemoryWarning方法:

首先調(diào)用[super didReceiveMemoryWarning]方法,然后檢查當(dāng)前視圖的父視圖是否為空,如果為空,則釋放掉一些不需要的數(shù)據(jù)。關(guān)于視圖界面的釋放不應(yīng)該在這個方法中,應(yīng)該放在viewDidUnload方法中。

3.dealloc方法:

對象釋放時調(diào)用,在這個方法中,要釋放掉所有的數(shù)據(jù)和輸出口。比如釋放輸出口:[xxx release];(不使用self關(guān)鍵字)

八、一些注意的地方

1.向集合(NSArray,NSDictionary等)添加對象時,被添加的對象會被執(zhí)行retain操作,當(dāng)從集合中移走對象或者集合對象被釋放時,集合中的對象會被執(zhí)行release操作。

2.要保證有多少個alloc、copy、multablecopy、retain消息,就要有多少個release或者autorelease,保證代碼平衡。

3.在程序中直接用@""創(chuàng)建的NSString對象,是常量,引用計數(shù)是-1,向它發(fā)送retain、release沒有效果。

4.在View中使用圖片時,大的圖片區(qū)域盡量使用小的圖片數(shù)據(jù)來填充,減小內(nèi)存占用。

5.[UIImage imageNamed:@""],次方法使用了系統(tǒng)緩存來緩存圖像,會長時間占用內(nèi)存,最好使用imageWithContentsOfFile方法。

6.數(shù)據(jù)要延遲加載,只在內(nèi)存中保留滿足需要的最少的數(shù)據(jù)和視圖元素,需要的時候再加載,不需要就馬上銷毀。

7.假如一個成員變量在property中使用了retain,當(dāng)使用self關(guān)鍵字對其賦值時,會對創(chuàng)建的對象再retain一次,造成內(nèi)存泄露。比如:self.xxx = [[XXXalloc] init];

對一個成員變量賦nil值時,self.xxx = nil,會調(diào)用xxx的release方法,并且將指針置空,xxx = nil,只是將指針置空。

9.在控制器中使用NSTimer會使當(dāng)前控制器引用計數(shù)加1,所以在控制器釋放之前,必須暫停和使定時器失效,否則控制器將不會被釋放。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容