iOS內(nèi)存管理機制的原理是引用計數(shù)厢岂,引用計數(shù)簡單來說就是統(tǒng)計一塊內(nèi)存的所有權(quán),當這塊內(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),當一塊內(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)認為合適的時間和地點釋放掉已經(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:當一個類的對象創(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;那么當對象1的內(nèi)存空間被釋放的時候遏餐,對象2便會成為野指針,再對對象2進行操作便會造成內(nèi)存錯誤失都。
copy:copy同樣是一個實例方法,只能由對象調(diào)用咳焚,返回一個新的對象信粮,它的作用是復制一個對象到一塊新的內(nèi)存空間上趁啸,舊內(nèi)存空間的引用計數(shù)不會變化,新的內(nèi)存空間的引用計數(shù)從0增加到1不傅,也就是說,雖然內(nèi)容一樣商虐,但實質(zhì)上是兩塊內(nèi)存崖疤,相當于克隆,一個變成兩個劫哼。其中copy又分為淺拷貝、深拷貝和真正的深拷貝眯亦,淺拷貝只是拷貝地址與retain等同般码;深拷貝是拷貝內(nèi)容,會新開辟新內(nèi)存板祝,與retain不一樣;真正的深拷貝是對于容器類來說的囊嘉,如數(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的基礎上再調(diào)用release嘲碧,便會造成過度釋放,使內(nèi)存崩潰钉迷;
autorelease:autorelease是一個實例方法钠署,同樣只能由對象調(diào)用,它的作用于release類似谐鼎,但不是立刻減1狸棍,相當于一個延遲的release,通常用于方法返回值的釋放草戈,如便利構(gòu)造器。autorelease會在程序走出自動釋放池時執(zhí)行丙猬,通常系統(tǒng)會自動生成自動釋放池(即使是MRC下),也可以自己設定自動釋放池茧球。
除了以上所述的關(guān)鍵字,還有一些方法會引起引用計數(shù)的變化弹灭,如UI中父視圖添加揪垄、移除子視圖,導航控制器或視圖控制器推出新的視圖控制器以及返回福侈,容器類(數(shù)組卢未、字典和集合)添加和移除元素。
當子視圖添加到父視圖上時伟墙,子視圖的引用計數(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ù)變化兆蕉。
文/且行且珍惜_iOS(簡書作者)
原文鏈接:http://www.reibang.com/p/d544cd964ced
著作權(quán)歸作者所有,轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)易稠,并標注“簡書作者”包蓝。
下面舉幾個例子:
1、聲明delegate為strong類型测萎,簡而言之,如果父VC持有子VC份乒,并設置子VC的delegate為self(也就是父VC)腕唧,這樣的結(jié)果就是子VC也間接持有了父VC,造成循環(huán)引用枣接,在Pop子VC的時候不會調(diào)用delloc月腋。
2、timer是否持有self榆骚,我們一般要執(zhí)行一個timer的時候會用(NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)ti? target:(id)aTarget?selector:(SEL)aSelector? userInfo:(id)userInfo? repeats:(BOOL)yesOrNo這里的aTarget一般是self,這時候就需要注意了捌省,如果在你退出的時候這個timer還在執(zhí)行的話由于這個timer會持有self碉钠,所以delloc也不會調(diào)用卷拘,這里可以用weakSelf代替self也是沒有問題的祝高。
3、最常見的就是block導致的循環(huán)引用乍赫,由于在重構(gòu)APP中用到了MVVM架構(gòu)陆蟆,使用了大量的信號機制,導致block到處飛(哈哈)改鲫,解決的最多的就是這種了林束,解決方法也很簡單,就是在block外聲明__weaktype(self) weakSelf =self讲弄,在block中用weakSelf就可以了依痊,還有就是在block中如果使用了成員變量的下劃線形式也要改成weakSelf.PropertyName的形式怎披。MVVM中定義了宏對@weakify(self)和@strongify(self)可以理解為__weaktype(self) weakSelf = self的簡化形式,可以拿來直接使用性宏。
4状飞、圖片沒釋放,instrument調(diào)試后酵使,發(fā)現(xiàn)沒被釋放的全是imageIO焙糟,差不多就知道了,把讀圖的方式穿撮,從[UIImageimageNamed:@""],改成imageWithContentsOfFile痪欲,就可以了攻礼。
5礁扮、使用GPUImage處理拍照的時候,內(nèi)存穩(wěn)定不明增長深员。是Xcode7.1的問題。遮糖。只在debug的時候?qū)е聝?nèi)存崩潰叠赐,release的時候并不會造成內(nèi)存溢出,所以可以不必管它芭概。
6罢洲、CoreFoundation對象(C對象) : 只要函數(shù)中包含了create\new\copy\retain等關(guān)鍵字, 那么這些方法產(chǎn)生的對象,就必須在不再使用的時候調(diào)用1次CFRelease或者其他release函數(shù)