iOS內(nèi)存管理機制的原理是引用計數(shù)询微,引用計數(shù)簡單來說就是統(tǒng)計一塊內(nèi)存的所有權(quán)似袁,當(dāng)這塊內(nèi)存被創(chuàng)建出來的時候淋叶,它的引用計數(shù)從0增加到1,表示有一個對象或指針持有這塊內(nèi)存雕拼,擁有這塊內(nèi)存的所有權(quán)纵东,如果這時候有另外一個對象或指針指向這塊內(nèi)存,那么為了表示這個后來的對象或指針對這塊內(nèi)存的所有權(quán)啥寇,引用計數(shù)加1變?yōu)?偎球,之后若有一個對象或指針不再指向這塊內(nèi)存時,引用計數(shù)減1辑甜,表示這個對象或指針不再擁有這塊內(nèi)存的所有權(quán)衰絮,當(dāng)一塊內(nèi)存的引用計數(shù)變?yōu)?,表示沒有任何對象或指針持有這塊內(nèi)存磷醋,系統(tǒng)便會立刻釋放掉這塊內(nèi)存猫牡。
其中在開發(fā)時引用計數(shù)又分為ARC(自動內(nèi)存管理)和MRC(手動內(nèi)存管理)。ARC的本質(zhì)其實就是MRC邓线,只不過是系統(tǒng)幫助開發(fā)者管理已創(chuàng)建的對象或內(nèi)存空間淌友,自動在系統(tǒng)認(rèn)為合適的時間和地點釋放掉已經(jīng)失去作用的內(nèi)存空間煌恢,原理是一樣的。雖然ARC操作起來很方便震庭,不但減少了代碼量症虑,而且降低了內(nèi)存出錯的概率,但因為ARC不一定會及時釋放归薛,所以程序有時候可能會占用內(nèi)存較大谍憔。而MRC若做得好,通過手動管理主籍,及時釋放掉不需要的內(nèi)存空間习贫,便可保證程序長時間運行在良好狀態(tài)上。
在MRC中會引起引用計數(shù)變化的關(guān)鍵字有:alloc千元,retain苫昌,copy,release幸海,autorelease祟身。(strong關(guān)鍵字只用于ARC,作用等同于retain)
alloc:當(dāng)一個類的對象創(chuàng)建物独,需要開辟內(nèi)存空間的時候袜硫,會使用alloc,alloc是一個類方法挡篓,只能用類調(diào)用婉陷,它的作用是開辟一塊新的內(nèi)存空間,并使這塊內(nèi)存的引用計數(shù)從0增加到1官研,注意秽澳,是新的內(nèi)存空間,每次用類alloc出來的都是一塊新的內(nèi)存空間戏羽,與上一次alloc出來的內(nèi)存空間沒有必然聯(lián)系担神,而且上一次alloc出來的內(nèi)存空間仍然存在,不會被釋放始花。
retain:retain是一個實例方法妄讯,只能由對象調(diào)用,它的作用是使這個對象的內(nèi)存空間的引用計數(shù)加1衙荐,并不會新開辟一塊內(nèi)存空間捞挥,通常于賦值是調(diào)用,如:
對象2=[對象1 retain]忧吟;表示對象2同樣擁有這塊內(nèi)存的所有權(quán)砌函。若只是簡單地賦值,如:對象2=對象1;那么當(dāng)對象1的內(nèi)存空間被釋放的時候讹俊,對象2便會成為野指針垦沉,再對對象2進行操作便會造成內(nèi)存錯誤。
copy:copy同樣是一個實例方法仍劈,只能由對象調(diào)用厕倍,返回一個新的對象,它的作用是復(fù)制一個對象到一塊新的內(nèi)存空間上贩疙,舊內(nèi)存空間的引用計數(shù)不會變化讹弯,新的內(nèi)存空間的引用計數(shù)從0增加到1,也就是說这溅,雖然內(nèi)容一樣组民,但實質(zhì)上是兩塊內(nèi)存,相當(dāng)于克隆悲靴,一個變成兩個臭胜。其中copy又分為淺拷貝、深拷貝和真正的深拷貝癞尚,淺拷貝只是拷貝地址與retain等同耸三;深拷貝是拷貝內(nèi)容,會新開辟新內(nèi)存浇揩,與retain不一樣仪壮;真正的深拷貝是對于容器類來說的,如數(shù)組類临燃、字典類和集合類(包括可變和不可變)睛驳,假設(shè)有一個數(shù)組類對象,普通的深拷貝會開辟一塊新內(nèi)存存放這個對象膜廊,但這個數(shù)組對象里面的各個元素的地址卻沒有改變也就是說數(shù)組元素只是進行了retain或者淺拷貝而已,并沒有創(chuàng)建新的內(nèi)存空間淫茵,而真正的深拷貝爪瓜,不但數(shù)組對象本身進行了深拷貝,連數(shù)組元素都進行了深拷貝匙瘪,即為各個數(shù)組元素開辟了新的內(nèi)存空間铆铆。
release:release是一個實例方法,同樣只能由對象調(diào)用丹喻,它的作用是使對象的內(nèi)存空間的引用計數(shù)減1薄货,若引用計數(shù)變?yōu)?則系統(tǒng)會立刻釋放掉這塊內(nèi)存。如果引用計數(shù)為0的基礎(chǔ)上再調(diào)用release碍论,便會造成過度釋放谅猾,使內(nèi)存崩潰;
autorelease:autorelease是一個實例方法,同樣只能由對象調(diào)用税娜,它的作用于release類似坐搔,但不是立刻減1,相當(dāng)于一個延遲的release敬矩,通常用于方法返回值的釋放概行,如便利構(gòu)造器。autorelease會在程序走出自動釋放池時執(zhí)行弧岳,通常系統(tǒng)會自動生成自動釋放池(即使是MRC下)凳忙,也可以自己設(shè)定自動釋放池,如:
@autoreleasepool{ obj= [[NSObject alloc]init]; [obj autorelease]; }
當(dāng)程序走出“}”時obj的引用計數(shù)就會減1.
除了以上所述的關(guān)鍵字禽炬,還有一些方法會引起引用計數(shù)的變化涧卵,如UI中父視圖添加、移除子視圖瞎抛,導(dǎo)航控制器或視圖控制器推出新的視圖控制器以及返回艺演,容器類(數(shù)組、字典和集合)添加和移除元素桐臊。
當(dāng)子視圖添加到父視圖上時胎撤,子視圖的引用計數(shù)加1,移除時引用計數(shù)減1断凶,若父視圖引用計數(shù)變?yōu)?內(nèi)存被釋放伤提,其所有的子視圖都會被release一次,即引用計數(shù)減1认烁,原則上只有這三種情況子視圖的引用計數(shù)會發(fā)生變化肿男,其他如父視圖引用計數(shù)的加減都不會影響到子視圖。
容器類的情況與視圖類似却嗡,添加元素舶沛,該元素引用計數(shù)加1,移除元素窗价,該元素引用計數(shù)減1如庭,容器引用計數(shù)變?yōu)?所占用內(nèi)存被釋放,容器所有元素release撼港,引用計數(shù)減1坪它,其他情況下容器本身的引用計數(shù)變化不會影響到容器內(nèi)元素的引用計數(shù)變化。
導(dǎo)航控制器或視圖控制器推出新的視圖控制器會使被推出的視圖控制器的引用計數(shù)加1帝牡,該視圖控制器返回的時候引用計數(shù)減1往毡,具體方法如下:
導(dǎo)航控制器推出視圖控制器調(diào)用方法:- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
返回時同樣用導(dǎo)航控制器調(diào)用方法:- (UIViewController *)popViewControllerAnimated:(BOOL)animated;
視圖控制器推出視圖控制器調(diào)用方法:- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion
返回時被推出的視圖控制器調(diào)用方法:- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion
應(yīng)注意:當(dāng)一個對象的引用計數(shù)變?yōu)?占用內(nèi)存被釋放時,會調(diào)用- (void)dealloc方法靶溜,所以如果在MRC下自定義類开瞭,必須在該方法里將該類中屬性關(guān)鍵字設(shè)置為retain或copy的屬性release一次懒震,以免造成內(nèi)存泄露,重寫方法不要忘記在第一行添加[super dealloc]惩阶;挎狸。
如果需要跟我交流的話:
※ Github: https://github.com/wsl2ls
※ 個人博客:https://wsl2ls.github.io
※ 簡書:http://www.reibang.com/u/e15d1f644bea
※ 微信公眾號:iOS2679114653
※ QQ:1685527540