一撼班、
內(nèi)存管理
內(nèi)存的作用: 存儲(chǔ)數(shù)據(jù).
1). 如何將數(shù)據(jù)存儲(chǔ)到內(nèi)存之中
聲明1個(gè)變量旗扑,將這個(gè)數(shù)據(jù)存儲(chǔ)進(jìn)去
2). 當(dāng)數(shù)據(jù)不再被使用的時(shí)候维哈,占用的內(nèi)存空間如何被釋放-
內(nèi)存中的五大區(qū)域
棧:局部變量例隆,當(dāng)局部變量的作用域被執(zhí)行完畢之后阔逼,這個(gè)局部變量就會(huì)被系統(tǒng)立即回收.
堆:OC對(duì)象论巍,使用C函數(shù)申請(qǐng)的空間
BSS段:未初始化的全局變量烛谊、靜態(tài)變量,一旦初始化就回收嘉汰,并轉(zhuǎn)存到數(shù)據(jù)段中.
數(shù)據(jù)段:已經(jīng)初始化的全局變量丹禀、靜態(tài)變量,知道程序結(jié)束的時(shí)候才會(huì)被回收.
代碼段:代碼鞋怀,程序結(jié)束的時(shí)候双泪,系統(tǒng)會(huì)自動(dòng)回收存儲(chǔ)在代碼段中的數(shù)據(jù).棧、BSS段密似、數(shù)據(jù)段焙矛、代碼段存儲(chǔ)在它們中的數(shù)據(jù)的回收,是系統(tǒng)自動(dòng)完成的.
-
分配在堆區(qū)的OC對(duì)象残腌,是肯定需要被回收的.
iPhone 內(nèi)存機(jī)制村斟,如果內(nèi)存占用內(nèi)存達(dá)到40M會(huì)受到系統(tǒng)警告,120M會(huì)閃退.存儲(chǔ)在堆中的OC對(duì)象,系統(tǒng)是不會(huì)自動(dòng)回收的废累,知道程序結(jié)束的時(shí)候才會(huì)被回收邓梅。
內(nèi)存管理的范圍:
只需要管理堆中的OC對(duì)象的回收,其他區(qū)域中的數(shù)據(jù)是系統(tǒng)自動(dòng)管理的.對(duì)象應(yīng)該什么時(shí)候被回收?
當(dāng)有人使用這個(gè)對(duì)象的時(shí)候邑滨,這個(gè)對(duì)象就不會(huì)被回收,只有在沒(méi)有人使用的時(shí)候才可以被回收.引用計(jì)數(shù)器
1). 每一次對(duì)象都有一個(gè)屬性日缨,叫做retainCount(引用計(jì)數(shù)器),類型是unsigned long占據(jù)八個(gè)字節(jié)。
引用計(jì)數(shù)器的作用: 用來(lái)記錄這個(gè)對(duì)象有多少人在使用它掖看,默認(rèn)情況下匣距,創(chuàng)建一個(gè)對(duì)象出來(lái)面哥,這個(gè)引用計(jì)數(shù)器的默認(rèn)值是1.
2). 當(dāng)多一個(gè)人使用的時(shí)候,這個(gè)引用計(jì)數(shù)器的值+1
3). 當(dāng)少一個(gè)人使用的時(shí)候毅待,這個(gè)引用計(jì)數(shù)器的值-1
4). 當(dāng)這個(gè)對(duì)象的引用計(jì)數(shù)器變?yōu)?的時(shí)候尚卫,代表這個(gè)無(wú)人使用,系統(tǒng)就會(huì)自動(dòng)回收它.如何操作引用計(jì)數(shù)器
1). 為對(duì)象發(fā)送1條retain消息尸红,對(duì)象的引用計(jì)數(shù)器就會(huì)加1吱涉,當(dāng)多一個(gè)人使用的對(duì)象的時(shí)候才發(fā)
2). 為對(duì)象發(fā)送1條release消息,對(duì)象的引用計(jì)數(shù)器就會(huì)減1外里,當(dāng)少一個(gè)人使用的對(duì)象的時(shí)候才發(fā)
3). 為對(duì)象發(fā)送一條retainCount消息怎爵,就可以了取到對(duì)象的引用計(jì)數(shù)器的值
4). 當(dāng)對(duì)象的引用計(jì)數(shù)器變?yōu)?的時(shí)候,對(duì)象就會(huì)被系統(tǒng)立即回收, 在對(duì)象被回收的時(shí)候盅蝗,會(huì)自動(dòng)調(diào)用對(duì)象的dealloc方法.-
內(nèi)存管理的分類
1).MRC(Manual Reference Counting)手動(dòng)引用計(jì)數(shù)鳖链,手動(dòng)內(nèi)存管理.
當(dāng)多一個(gè)人使用對(duì)象的時(shí)候,要求程序員手動(dòng)的發(fā)送retain消息;反之.
2). ARC(Automatic Reference Counting)自動(dòng)引用計(jì)數(shù)墩莫,自動(dòng)內(nèi)存管理.
系統(tǒng)自動(dòng)的在合適的地方發(fā)送retain realse消息.- 學(xué)習(xí)MRC理由:面試芙委、早期的App開(kāi)發(fā)使用的是MRC技術(shù)、ARC是基于MRC的.
二狂秦、MRC
Xcode默認(rèn)的開(kāi)發(fā)方式是ARC模式.
關(guān)閉ARC:Project->選中工程->Build Settings-> All->Apple LLVM7.0 - Language - Objective C ->Objective-C Automatic Reference Counting,設(shè)置為NO.-
當(dāng)對(duì)象的引用計(jì)數(shù)器變?yōu)?的時(shí)候灌侣,系統(tǒng)會(huì)自動(dòng)回收對(duì)象. 在系統(tǒng)回收對(duì)象的時(shí)候,會(huì)自動(dòng)的調(diào)用對(duì)象的dealloc方法.
1). 重寫類的dealloc方法, 必須要調(diào)用父類的dealloc方法,要放在最后一句代碼的位置故痊。- (void)dealloc{ NSLog(@"名字叫XXX消失了"); [super dealloc]; }
-
測(cè)試引用計(jì)數(shù)器
1). 新創(chuàng)建一個(gè)對(duì)象顶瞳,這個(gè)對(duì)象的引用計(jì)數(shù)器的值默認(rèn)是1.Person *p1 = [[Person alloc] init]; [p1 retainCount];// 獲取引用計(jì)數(shù)器的值 [p1 retain];//為引用計(jì)數(shù)器加1 [p1 release];//為引用計(jì)數(shù)器減1
2). 當(dāng)對(duì)象的引用計(jì)數(shù)器變?yōu)?的時(shí)候,對(duì)象就會(huì)被系統(tǒng)立即回收愕秫,并自動(dòng)調(diào)用dealloc方法.
3). 為對(duì)象發(fā)送這個(gè)return消息慨菱,對(duì)象的引用計(jì)數(shù)器就會(huì)加1 為對(duì)象發(fā)送release消息并不是回收對(duì)象,而是讓對(duì)象的引用計(jì)數(shù)器減1戴甩,當(dāng)對(duì)象的引用計(jì)數(shù)器的值變?yōu)?的時(shí)候符喝,對(duì)象才會(huì)被系統(tǒng)立即回收.
內(nèi)存管理的重點(diǎn)
1). 什么時(shí)候?yàn)閷?duì)象發(fā)送retain消息
當(dāng)多一個(gè)人使用這個(gè)對(duì)象的時(shí)候,應(yīng)該先為這個(gè)對(duì)象發(fā)送retain消息
2). 什么時(shí)候?yàn)閷?duì)象發(fā)送release消息
當(dāng)少一個(gè)人使用這個(gè)對(duì)象的時(shí)候甜孤,應(yīng)該為這個(gè)對(duì)象發(fā)送一條release消息
在ARC機(jī)制下协饲,retain、release缴川、dealloc這些方法無(wú)法調(diào)用內(nèi)存管理的原則
1). 有對(duì)象的創(chuàng)建茉稠,就要匹配一個(gè)release
2). retain和release的調(diào)用次數(shù)要匹配
3). 誰(shuí)用誰(shuí)retain,誰(shuí)不用誰(shuí)release
4). 只有在多一個(gè)人用的時(shí)候才retain把夸,少一個(gè)人使用的時(shí)候才release而线,有始有終,有加就有減。野指針
C語(yǔ)言中的野指針: 定義一個(gè)指針變量膀篮,沒(méi)有初始化嘹狞,這個(gè)指針變量的值時(shí)一個(gè)垃圾值,指向一塊隨機(jī)的空間
OC中的野指針:指針指向的對(duì)象已經(jīng)被回收了.內(nèi)存回收的本質(zhì):
申請(qǐng)一個(gè)變量誓竿,實(shí)際上就是向系統(tǒng)申請(qǐng)指定字節(jié)數(shù)的空間磅网,這段空間就不會(huì)分配給別人了,當(dāng)變量被回收的時(shí)候筷屡,代表變量占用的字節(jié)空間可以分配給別人了涧偷,但是這個(gè)字節(jié)空間中存儲(chǔ)的數(shù)據(jù)還在。
對(duì)象回收的本質(zhì):指的是對(duì)象占用的空間可以分配給別人了速蕊,當(dāng)這個(gè)對(duì)象占用的空間沒(méi)有分配給別人之前嫂丙,其實(shí)對(duì)象數(shù)據(jù)還在.僵尸對(duì)象
1個(gè)已經(jīng)被釋放的對(duì)象,但是這個(gè)對(duì)象所占空間還沒(méi)有分配給別人规哲。通過(guò)野指針訪問(wèn)僵尸對(duì)象的時(shí)候,有可能沒(méi)問(wèn)題诽表,也有可能有問(wèn)題唉锌。當(dāng)僵尸對(duì)象占用的空間還沒(méi)有分配給別人使用的時(shí)候,還是可以的;反之竿奏。-
我們認(rèn)為只要對(duì)象成為了僵尸對(duì)象袄简,無(wú)論如何都應(yīng)該不允許訪問(wèn)了。如果訪問(wèn)的是僵尸對(duì)象泛啸,無(wú)論如何都報(bào)錯(cuò)绿语。
僵尸對(duì)象的實(shí)時(shí)檢查機(jī)制,可以將這個(gè)機(jī)制打開(kāi)候址,打開(kāi)之后吕粹,只要訪問(wèn)的是僵尸對(duì)象,無(wú)論空間是否分配岗仑,就會(huì)報(bào)錯(cuò)匹耕。停止運(yùn)行按鈕右側(cè)的Target切換點(diǎn)擊-> Edit Scheme...->Run->Diagnostics->勾選Enable Zomble Objects.
為什么不默認(rèn)打開(kāi)僵尸對(duì)象檢測(cè)
一旦打開(kāi)僵尸對(duì)象檢測(cè),那么在每訪問(wèn)一個(gè)僵尸對(duì)象的時(shí)候荠雕,都會(huì)先檢測(cè)這個(gè)對(duì)象是否是一個(gè)僵尸對(duì)象稳其,這樣是極其消耗性能的.如何避免僵尸對(duì)象錯(cuò)誤?
當(dāng)一個(gè)指針成為野指針以后炸卑,將這個(gè)指針的值設(shè)置為nil.
當(dāng)一個(gè)指針為nil時(shí)既鞠,通過(guò)這個(gè)指針調(diào)用對(duì)象的方法的時(shí)候,不會(huì)報(bào)錯(cuò)盖文,只是沒(méi)有任何反應(yīng)培漏,但是如果通過(guò)指針直接訪問(wèn)屬性就會(huì)報(bào)錯(cuò).無(wú)法復(fù)活一個(gè)僵尸對(duì)象
三、
內(nèi)存泄漏
指的是一個(gè)對(duì)象沒(méi)有被及時(shí)的回收另萤,在該回收的時(shí)候沒(méi)有及時(shí)回收,一直駐留在內(nèi)存中蒋失,知道程序結(jié)束的時(shí)候才結(jié)束.單個(gè)對(duì)象的內(nèi)存泄漏情況
1). 有對(duì)象的創(chuàng)建,而沒(méi)有對(duì)應(yīng)的release
2). retain的次數(shù)和release的次數(shù)不匹配
3). 在不適當(dāng)?shù)臅r(shí)候?yàn)橹羔樫x值為nil
4). 在方法中為傳入的參數(shù)不適當(dāng)?shù)膔etain如何保證單個(gè)對(duì)象可以被回收
1). 有對(duì)象的創(chuàng)建桐玻,就必須要匹配一個(gè)release
2). retain和release次數(shù)一定要匹配
3). 只有在指針成為野指針時(shí)才賦值為nil
4). 在方法中不要隨意的為傳入的對(duì)象retain-
多個(gè)對(duì)象的內(nèi)存管理
1). 當(dāng)屬性是一個(gè)OC對(duì)象的時(shí)候篙挽,setter方法的寫法.
將傳進(jìn)來(lái)的對(duì)象賦值給當(dāng)前對(duì)象的屬性,代表傳入的對(duì)象多一個(gè)人使用镊靴,所以我們應(yīng)該先為這個(gè)傳入的對(duì)象發(fā)送一條retain消息再賦值.當(dāng)當(dāng)前對(duì)象銷毀的時(shí)候铣卡,代表屬性指向的對(duì)象少一個(gè)人使用,就應(yīng)該在dealloc中release.
代碼寫法:- (void)setCar:(Car *)car{ _car = [car retain]; } - (void)dealloc{ [_car release]; [super dealloc]; }
2). 當(dāng)屬性是一個(gè)OC對(duì)象的時(shí)候偏竟,setter方法按照上面寫還是有bug.
當(dāng)為對(duì)象的這個(gè)屬性多次賦值的時(shí)候煮落,就會(huì)發(fā)生內(nèi)存泄漏,發(fā)生泄漏的原因: 當(dāng)為屬性賦值的時(shí)候踊谋,代表就對(duì)象少一個(gè)人用蝉仇,新對(duì)象多一個(gè)人用,應(yīng)該release殖蚕,舊對(duì)象retain新的.
代碼寫法:- (void)setCar:(Car *)car{ [_car release]; _car = [car retain]; } - (void)dealloc{ [_car release]; [super dealloc]; }
3). 出現(xiàn)僵尸對(duì)象的原因轿衔,新舊對(duì)象是同一個(gè),當(dāng)發(fā)現(xiàn)新舊對(duì)象是同一個(gè)的時(shí)候睦疫,什么都不用做.只有當(dāng)新舊對(duì)象不是同一對(duì)象的時(shí)候害驹,才release舊的.
最終代碼寫法:- (void)setCar:(Car *)car{ if(_car != car){ [_car release]; _car = [car retain]; } } - (void)dealloc{ [_car release]; [super dealloc]; }
4). 特別注意: 內(nèi)存管理的范圍是OC對(duì)象,這個(gè)屬性的setter方法才要像上面那樣寫.
-
@property參數(shù)
1). 作用
a. 自動(dòng)生成私有屬性
b. 自動(dòng)生成這個(gè)屬性的getter/setter的聲明
c. 自動(dòng)生成這個(gè)屬性的getter/setter的實(shí)現(xiàn)
特別播報(bào): 生成的setter方法實(shí)現(xiàn)中蛤育,無(wú)論什么類型宛官,都是直接賦值.
2). @property參數(shù)
a. @property可以帶參數(shù)
@property(參數(shù)1, 參數(shù)2, 參數(shù)3...)數(shù)據(jù)類型 名稱;
b. @property的四組參數(shù)
a). 與多線程相關(guān)的兩個(gè)參數(shù)
atomic/nonatomic
b). 與生成的setter方法的實(shí)現(xiàn)相關(guān)的參數(shù)
assign/retain
c). 與生成只讀、讀寫相關(guān)的參數(shù)
readonly/readwrite
d). 與生成getter/setter方法名相關(guān)的參數(shù)
getter/setter3). 與多線程相關(guān)的參數(shù)(atomic/nonatomic)
atomic: 默認(rèn)值,這個(gè)時(shí)候生成的setter方法的代碼就會(huì)被加上一把線程安全鎖.特點(diǎn): 安全瓦糕、效率低
nonatomic: 生成的setter方法的代碼不會(huì)加線程安全鎖底洗,但是效率高.
建議要效率,選擇使用nonatomic
4). 與生成的setter方法的實(shí)現(xiàn)相關(guān)的參數(shù)(assign/retain)
assign: 默認(rèn)值刻坊,生成的setter方法實(shí)現(xiàn)就是直接賦值
retain: 生成的setter方法的方法實(shí)現(xiàn)就是標(biāo)準(zhǔn)的MRC內(nèi)存管理代碼枷恕,先判斷新舊對(duì)象是否為同一對(duì)象,如果不是就release舊的谭胚,retain新的.
當(dāng)屬性的類型是OC對(duì)象類型的時(shí)候就使用retain徐块,當(dāng)屬性的類型實(shí)時(shí)非OC對(duì)象的時(shí)候就使用assign.
注意: retain只是生成標(biāo)準(zhǔn)的setter方法為標(biāo)準(zhǔn)的MRC內(nèi)存管理代碼,不會(huì)自動(dòng)的在dealloc中生成release的代碼灾而,需要手動(dòng)添加.
5). 與生成只讀胡控、讀寫相關(guān)的參數(shù)readonly/readwrite)
readwrite: 默認(rèn)值,代表同時(shí)生成getter/setter
readonly: 只會(huì)生成getter旁趟,不會(huì)生成setter6). 與生成getter/setter方法名相關(guān)的參數(shù)(getter/setter)
默認(rèn)情況下: @property生成的getter/setter是標(biāo)準(zhǔn)類型的,其實(shí)我們可以通過(guò)參數(shù)來(lái)指定@property生成的方法的名字
setter: setter=setter方法名字 用來(lái)指定@property生成的setter的方法的名字昼激,注意,setter方法是帶參數(shù)的,要加一個(gè)冒號(hào)
getter: getter=getter方法名字 用來(lái)指定@property生成的getter方法的名字
注意: 如果使用setter/getter修改了生成方法的名字橙困,在使用點(diǎn)語(yǔ)法的時(shí)候瞧掺,編譯器在轉(zhuǎn)換的時(shí)候轉(zhuǎn)換成修改后的名字的代碼.
使用建議:
a. 無(wú)論什么情況都不要改setter方法的名字,默認(rèn)情況下生成的名字已經(jīng)是最標(biāo)準(zhǔn)的了
b. 什么修改getter方法的名字凡傅,當(dāng)屬性的類型是一個(gè)BOOL類型的時(shí)候辟狈,就修改這個(gè)getter的名字以is開(kāi)頭,提高代碼的閱讀性. @class
1). 當(dāng)兩個(gè)類相互引用的時(shí)候夏跷,當(dāng)Person.h中包含Book.h哼转,而B(niǎo)ook.h中又包含Person.h,這個(gè)時(shí)候就會(huì)造成無(wú)限遞歸槽华,而導(dǎo)致無(wú)法編譯通過(guò).
2). 解決方案:
其中一個(gè)類中不要使用#import引入對(duì)方的頭文件壹蔓,而是使用@class 類名,來(lái)標(biāo)注這是一個(gè)類猫态,這樣就可以在不引入對(duì)方頭文件的情況下佣蓉,告訴編譯器這是一個(gè)類。
在.m文件當(dāng)中再#import對(duì)方的頭文件
3). @class與#import的區(qū)別
a. #import是將指定的文件的內(nèi)容拷貝到寫指令的地方
b. @class并不會(huì)拷貝任何內(nèi)容懂鸵,只是告訴編譯器這是一個(gè)類偏螺,這樣編譯器在編譯的時(shí)候才可以指定這是一個(gè)類。循環(huán)retain
1). 當(dāng)兩個(gè)對(duì)象相互引用的時(shí)候
A對(duì)象的屬性是B對(duì)象匆光,B對(duì)象的屬性是A對(duì)象.如果兩邊都使用retain,就會(huì)發(fā)生內(nèi)存泄漏
2). 解決方案
一段使用retain酿联,一段使用assign终息,使用assign的那一段在dealloc中不再需要release.