我想大部分人都知道通常一個(gè)程序員會(huì)具有的美德循捺。當(dāng)然了幕垦,有三種:懶惰,暴躁已旧,傲慢。 ----Perl語言發(fā)明者Larry Wall
我想不管是iOS的,還是Java的初學(xué)者,內(nèi)存算得上心中的一個(gè)永遠(yuǎn)抹不去的痛吧,當(dāng)時(shí)作為初學(xué)者的我也是一度苦惱,不知道該如何理解這個(gè)內(nèi)存,隨著不斷的學(xué)習(xí),自己對(duì)內(nèi)存也有更深的了解.
內(nèi)存
說到內(nèi)存,不能不說一下內(nèi)存的分區(qū),內(nèi)存總共分為五大區(qū),分別是棧區(qū) 堆區(qū) 靜態(tài)區(qū) 常量區(qū) 代碼區(qū),五個(gè)區(qū)是按照內(nèi)存地址從大到小分配的.
其實(shí)我們最常用的還是棧區(qū)和堆區(qū)的內(nèi)存,然后首先來看一下棧區(qū)內(nèi)存的特點(diǎn)吧,
1.棧區(qū)內(nèi)存
1.局部變量的存儲(chǔ)空間基本都是棧區(qū),局部變量在函數(shù),循環(huán),分支中定義
2.在棧區(qū)的存儲(chǔ)空間由高向低分配,從低向高存儲(chǔ).!!
3.棧區(qū)內(nèi)存由系統(tǒng)負(fù)責(zé)分配和回收,程序員開發(fā)者沒有管理權(quán)限.
4.當(dāng)函數(shù),循環(huán),分支執(zhí)行結(jié)束后,局部變量的生命周期就結(jié)束了.之后不能再進(jìn)行使用,由系統(tǒng)銷毀
5.棧底,棧頂:棧底是棧區(qū)內(nèi)存的起始位置,先定義的變量所占用的內(nèi)存,從棧底開始分配,后定義的變量所占用的內(nèi)存,逐漸向棧頂分配.
6.入棧,出棧:入棧,定義新的局部變量,分配存儲(chǔ)空間.出棧,局部變量被銷毀,存儲(chǔ)空間被收回.
7.棧的特點(diǎn):先進(jìn)后出,后進(jìn)先出.例如:子彈夾添加子彈,打出子彈.
2.堆區(qū)內(nèi)存
1.由開發(fā)者負(fù)責(zé)分配和回收.
2.忘記回收會(huì)造成泄漏.
3.程序運(yùn)行結(jié)束后,需要及時(shí)回收堆內(nèi)存,但是如果不能及時(shí)回收堆內(nèi)存程序運(yùn)行期間可能會(huì)因?yàn)閮?nèi)存泄漏造成堆內(nèi)存被全部使用,導(dǎo)致程序無法使用.
3.常量區(qū)內(nèi)存
1.常量存儲(chǔ)在常量區(qū),例如:常量數(shù)字,常量字符串,常量字符,
2.常量區(qū)存儲(chǔ)空間由系統(tǒng)分配和回收
3.程序運(yùn)行結(jié)束后,常量區(qū)的存儲(chǔ)空間被回收
4.常量區(qū)的數(shù)據(jù)只能讀取,不能修改,修改的話會(huì)造成崩潰.
4.靜態(tài)區(qū)內(nèi)存
1.全局變量,使用static修飾的局部變量,都存儲(chǔ)在靜態(tài)區(qū).
2.靜態(tài)區(qū)的存儲(chǔ)空間由系統(tǒng)分配和回收.
3.程序運(yùn)行結(jié)束后,靜態(tài)區(qū)的存儲(chǔ)空間被回收,靜態(tài)變量的生命周期和程序一樣長(zhǎng).
4.靜態(tài)變量只能初始化一次,在編譯時(shí)進(jìn)行初始化,運(yùn)行時(shí)可以修改值
5.靜態(tài)變量如果沒有設(shè)置初始值,默認(rèn)為0.
5.代碼區(qū)內(nèi)存
1.由系統(tǒng)分配和回收
2.程序運(yùn)行結(jié)束之后,由系統(tǒng)回收分配過的內(nèi)存存儲(chǔ)空間
內(nèi)存管理
上面我們說到了內(nèi)存的五大區(qū),現(xiàn)在我從內(nèi)存的管理來說一下內(nèi)存,為什么我們開發(fā)應(yīng)用的時(shí)候要要注意內(nèi)存呢?由于移動(dòng)設(shè)備的內(nèi)存有限,所以每一個(gè)APP應(yīng)用程序的內(nèi)存也是有限的,App所占用的內(nèi)存較多時(shí),系統(tǒng)就會(huì)發(fā)出內(nèi)存警告.為了節(jié)省內(nèi)存的使用.所以我們就要使用到內(nèi)存管理,就是當(dāng)對(duì)象不再被使用的時(shí)候,我們要對(duì)其內(nèi)存及時(shí)的進(jìn)行回收.
內(nèi)存管理這一塊我要說的是我們?cè)贛RC(Manual Reference Counting)環(huán)境下需要手動(dòng)管理堆區(qū)的內(nèi)存,進(jìn)行release操作等,但是在 ARC(Automatic Reference Counting)環(huán)境下,我們不需要手動(dòng)管理我們的內(nèi)存.首先我們需要看一下如何切換ARC環(huán)境和MRC環(huán)境.
首選我們需要打開工程的配置面板,然后Build setting面板中搜索auto(自動(dòng))關(guān)鍵的字樣,然后就可以查找到ARC環(huán)境的切換選項(xiàng)了
下面我就說一下內(nèi)存管理的核心,內(nèi)存管理的核心就是引用計(jì)數(shù)的加減,引用計(jì)數(shù)相當(dāng)于對(duì)象的一個(gè)屬性,當(dāng)然了,這個(gè)屬性是不需要我們手動(dòng)創(chuàng)建的,系統(tǒng)會(huì)幫每一個(gè)對(duì)象進(jìn)行創(chuàng)建的.
引用計(jì)數(shù)器的作用:用來判斷對(duì)象是否應(yīng)該回收內(nèi)存空間,當(dāng)引用計(jì)數(shù)器為0時(shí),此時(shí)需要回收對(duì)象的內(nèi)存空間.
引用計(jì)數(shù)器的操作:
retain 引用計(jì)數(shù)器 +1
release 引用計(jì)數(shù)器 -1
retainCount 得到引用計(jì)數(shù)器的值
如果一個(gè)對(duì)象被釋放的時(shí)候,引用計(jì)數(shù)為0了,就會(huì)使用一個(gè)方法,析構(gòu)函數(shù)dealloc;
然后,我們就看一下內(nèi)存管理的黃金法則,
內(nèi)存黃金法則:
誰alloc,誰release!(包括new);
誰retain,誰release!
(retain) 引用計(jì)數(shù)+1
(release) 引用計(jì)數(shù)-1
誰copy,誰release!
野指針與內(nèi)存泄露
說到內(nèi)存管理就不得不說一下內(nèi)存泄露和野指針問題.
內(nèi)存泄漏
用動(dòng)態(tài)存儲(chǔ)分配函數(shù)動(dòng)態(tài)開辟的空間召娜,在使用完畢后未釋放运褪,結(jié)果導(dǎo)致一直占據(jù)該內(nèi)存單元,不能被任何程序再次使用玖瘸,直到程序結(jié)束秸讹。即所謂內(nèi)存泄漏。
注意:內(nèi)存泄漏是指堆內(nèi)存的泄漏店读。
簡(jiǎn)單的說就是申請(qǐng)了一塊內(nèi)存空間嗦枢,使用完畢后沒有釋放掉。它的一般表現(xiàn)方式是程序運(yùn)行時(shí)間越長(zhǎng)屯断,占用內(nèi)存越多文虏,最終用盡全部?jī)?nèi)存,整個(gè)系統(tǒng)崩潰殖演。由程序申請(qǐng)的一塊內(nèi)存氧秘,且沒有任何一個(gè)指針指向它,那么這塊內(nèi)存就泄露了趴久。
野指針
“野指針”不是NULL指針丸相,是未初始化或未清零的指針,他指向的內(nèi)存地址不是程序員想要的彼棍。人們一般不會(huì)錯(cuò)用NULL指針灭忠,因?yàn)橛胕f語句很容易判斷座硕。但是“野指針”是很危險(xiǎn)的弛作,if語句對(duì)它不起作用。野指針的成因主要有兩種:
一华匾、指針變量沒有被初始化映琳。任何指針變量剛被創(chuàng)建時(shí)不會(huì)自動(dòng)成為NULL指針,它的缺省值是隨機(jī)的蜘拉,它會(huì)亂指一氣萨西。所以,指針變量在創(chuàng)建的同時(shí)應(yīng)當(dāng)被初始化旭旭,要么將指針設(shè)置為NULL谎脯,要么讓它指向合法的內(nèi)存。
二您机、指針p被free或者delete之后穿肄,沒有置為NULL年局,讓人誤以為p是個(gè)合法的指針。別看free和delete的名字惡狠狠的(尤其是delete)咸产,它們只是把指針?biāo)傅膬?nèi)存給釋放掉矢否,但并沒有把指針本身干掉。通常會(huì)用語句if (p != NULL)進(jìn)行防錯(cuò)處理脑溢。很遺憾僵朗,此時(shí)if語句起不到防錯(cuò)作用,因?yàn)榧幢鉷不是NULL指針屑彻,它也不指向合法的內(nèi)存塊验庙。
#pragma mark---研究簡(jiǎn)單的野指針---
Person *p = [Person new];
//使用KVC對(duì)屬性進(jìn)行賦值
[p setValue:@"棟棟" forKey:@"name"];
[p retain];
//調(diào)用方法
[p eat];
//釋放
[p release];
//此處是野指針,在正常的情況下是可以訪問的.如果想要查找野指針,就要打開僵尸模式,editscheme->diagnostics->zombie
[p eat];
[p release];//如果不release就會(huì)產(chǎn)生內(nèi)存泄漏.
上面說到了僵尸模式,然后我們就看看如何開啟僵尸模式,檢測(cè)野指針的存在.
拷貝
對(duì)于拷貝,也是讓大多數(shù)人頭疼的,因?yàn)榭截惪局局桶炎约航o弄糊涂了,所以,今天最后我還要說一下這個(gè)copy相關(guān)的問題,首先拷貝分為可變拷貝和不可變的拷貝.來看一下實(shí)際的范例吧
NSString * p1 =@"Jobs";
NSString *p2 = [NSString stringWithFormat:@"wang"];
NSString *p3 = [[NSString alloc]initWithString:p1];
NSString *p4 = [NSString stringWithString:p1];
NSLog(@"%@ ----- %p",p1,p1);
NSLog(@"%@ ----- %p",p2,p2);
NSLog(@"%@ ----- %p",p3,p3);
NSLog(@"%@ ----- %p",p4,p4);
NSLog(@"%p",p1);
然后打印結(jié)果是這樣的..
這樣我們不難發(fā)現(xiàn),p3和p4只是指針的重指向而已.
不可變拷貝
下面就是不可變拷貝,當(dāng)我們這樣使用的時(shí)候 程序是會(huì)crash掉的,雖然p2的類型是可變的字符串對(duì)象,由于p2是p1進(jìn)行不可變出來的,其實(shí)相當(dāng)于p2指向了p1,通過地址我們不難發(fā)現(xiàn)p1和p2指針指向的是相同的一個(gè)地址.
NSString * p1 =@"Jobs";
NSLog(@"%p",p1);
//可變字符串對(duì)象 指向 不可變拷貝的數(shù)據(jù)
NSMutableString * p2 = [p1 copy];
NSLog(@"%p",p2);
//如果上面使用的是 copy,那么此處就會(huì)crash,如果使用的是,mutableCopy那么將會(huì)改變
[p2 appendString:@"steve"];
</br>
可變拷貝
如果我們將上面的過程換成可變的拷貝,然后再進(jìn)行字符串的拼接會(huì)有什么情況的產(chǎn)生呢?
NSString * p1 =@"Jobs";
NSLog(@"%p",p1);
//可變字符串對(duì)象 指向 不可變拷貝的數(shù)據(jù)
NSMutableString * p2 = [p1 mutableCopy];
NSLog(@"%p",p2);
//如果上面使用的是 copy,那么此處就會(huì)crash,如果使用的是,mutableCopy那么將會(huì)改變
[p2 appendString:@"steve"];
通過上面的兩個(gè)實(shí)例,我們簡(jiǎn)單的對(duì)可變拷貝和不可變拷貝做一下總結(jié),用于以后的工作中
上述總結(jié):
copy(不可變拷貝),如果使用了不可變拷貝,那么接收的對(duì)象不管是可變對(duì)象還是不可變的對(duì)象,都不能改變拷貝過來的內(nèi)容
mutableCopy(可變拷貝),如果使用了可變拷貝,那么接收對(duì)象不管是可變的還是不可變的對(duì)象,都可以改變拷貝過來的內(nèi)容
淺拷貝和深拷貝
對(duì)于淺拷貝和深拷貝,其實(shí)就是拷貝對(duì)象的區(qū)別,淺拷貝拷貝的是地址,而深拷貝拷貝的是對(duì)象的本身.
#pragma mark---怎么判斷深拷貝----
//通過兩個(gè)對(duì)象的地址來判斷,如果地址不同,那么就是深拷貝.
#pragma mark---怎么判斷淺拷貝----
//通過兩個(gè)對(duì)象的地址來判斷,如果地址相同,那么就是淺拷貝.
這里我給大家附加上一道面試題,來提高大家對(duì)copy的理解
#pragma mark--- 面試題 ---
//1.你怎么樣理解深拷貝和淺拷貝.
//淺拷貝:就如同人和影子,在內(nèi)存,人沒影子就沒了,影子沒了人就沒了.就是操作的同一個(gè)空間.
//深拷貝:就如同人和克隆,在內(nèi)存中,人沒了克隆還在,克隆沒有,人還在.就是操作的不同的空間.
//2.如何對(duì)深拷貝和淺拷貝進(jìn)行內(nèi)存釋放
//淺拷貝:釋放一個(gè)即可.因?yàn)獒尫拍膫€(gè)對(duì)象,都是同一個(gè)空間.
//深拷貝:釋放全部,因?yàn)槭莾蓚€(gè)對(duì)象,而且是兩個(gè)空間.
今天我就先說到這,后期將說一下,對(duì)象屬性的實(shí)現(xiàn)原理,如果您喜歡這篇文章就點(diǎn)個(gè) '喜歡' 吧,不要打賞,只求 ' 喜歡 ',謝謝各位看官了.
參考博客原文:http://blog.csdn.net/dangercheng/article/details/12618161