iOS目前已經(jīng)是ARC 時(shí)代。但對(duì)于要想了解ARC的內(nèi)存管理機(jī)制某筐,還是依舊需要對(duì)MRC時(shí)代的內(nèi)存管理機(jī)制有深刻的理解才能掌握ARC比搭。
話(huà)不多說(shuō),直接開(kāi)干南誊。
第一點(diǎn)
我們?cè)趧?chuàng)建一個(gè)對(duì)象的時(shí)候身诺,不論是ARC 還是MRC 一般都是這樣創(chuàng)建的
NSMutableArray aArray = [[NSArray alloc] init];
NSMutableArray aArray = aArray.mutableCopy;
(一般情況下: 后面會(huì)討論例外情況)
alloc 對(duì)象分配空間后,引用計(jì)數(shù)為1
retain 對(duì)象的引用計(jì)數(shù)+1抄囚,不會(huì)對(duì)對(duì)象分配空間
copy copy 一個(gè)對(duì)象變成新的對(duì)象(新內(nèi)存地址) 引用計(jì)數(shù)為1 原來(lái)對(duì)象計(jì)數(shù)不變(就是說(shuō)霉赡,copy是將copy的對(duì)象復(fù)制一份,作為新的對(duì)象幔托,然后分配空間穴亏,賦予新地址,再將其引入計(jì)數(shù)+1)
release 對(duì)象的引用計(jì)數(shù)-1 如果為該對(duì)象的引入計(jì)數(shù)減到 0 時(shí)重挑,該對(duì)象釋立即放內(nèi)存
autorelease 對(duì)象引用計(jì)數(shù)-1 如果為0不馬上釋放嗓化,最近一個(gè)個(gè)pool時(shí)釋放
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]); //打印對(duì)象引入計(jì)數(shù)的個(gè)數(shù)
第二點(diǎn)(內(nèi)存管理的原則)
內(nèi)存管理的原則 就是最終的引用計(jì)數(shù)要平衡如果最后引用計(jì)數(shù)大于0 則會(huì)內(nèi)存泄露
__weak typeof(self) weakSelf = self
[MM_Factory addLeftButtonForVC:self imgName:@"left_mm_oa" hightligthImgName:nil title:nil clickedHandler:^{
[weakSelf.navigationController popViewControllerAnimated:YES];
}];
如果這里的weakSelf 還是self 則就會(huì)造成引入計(jì)數(shù)加1(block中如果不作為copy處理,會(huì)造成原有對(duì)象引入技術(shù)加1)所以會(huì)造成內(nèi)存泄露谬哀。
如果引用 計(jì)數(shù)等于 0 還對(duì)該對(duì)象進(jìn)行操作刺覆,則會(huì)出現(xiàn)內(nèi)存訪問(wèn)失敗,crash 所以盡量設(shè)置為nil
這兩個(gè)問(wèn)題都很?chē)?yán)重玻粪,所以請(qǐng)一定注意內(nèi)存釋放和不用過(guò)后設(shè)置為nil
第三點(diǎn) (成員變量與屬性)
實(shí)際情況并非上面那么簡(jiǎn)單隅津,你可能需要在一個(gè)函數(shù)里調(diào)用另一個(gè)函數(shù)分配的變量這時(shí)候有兩個(gè)選擇: 類(lèi)成員變量和使用屬性
@interface TestMem: NSObject {
TestObject *m_testObject ; //**成員變量**
TestObject *testObject; //**成員變量**
}
成員變量與上面的內(nèi)存管理是一致的诬垂,只是在不同的函數(shù)里要保持引用計(jì)數(shù)加減的平衡所以要你要每次分配的時(shí)候檢查是否上次已經(jīng)分配了。是否還能調(diào)用什么時(shí)候用屬性?
- 把成員做為public.(公開(kāi)對(duì)象)
- outlet 一般聲明為屬性( 這個(gè)內(nèi)存于系統(tǒng)控制伦仍,但我們還是應(yīng)該做一樣操作结窘,后面會(huì)講)
- 如果很多函數(shù)都需要改變這個(gè)對(duì)象 ,或這個(gè)函數(shù)會(huì)觸發(fā)很多次充蓝,建議使用屬性
我們看看屬性函數(shù)展開(kāi)后是什么樣子:
**
**// assign
**
-(void)setTestObject :(id)newValue{
testObject= newValue;
}
**// retain
**
-(void)setTestObject :(id)newValue{
if (testObject!= newValue) {
[testObject release];
testObject= [newValue retain];
}
}
**// copy
**
-(void)setTestObject :(id)newValue{
if (testObject != newValue) {
[testObject release];
testObject = [newValue copy];
}
}
asssign 相于于指針賦值隧枫,不對(duì)引用計(jì)數(shù)進(jìn)行操作,注意原對(duì)象不用了谓苟,一定要把這個(gè)設(shè)置為nil
retain 相當(dāng)于對(duì)原對(duì)象的引用計(jì)數(shù)加1
copy 不對(duì)原對(duì)象的引用計(jì)數(shù)改變官脓,生成一個(gè)新對(duì)象引用計(jì)數(shù)為1
注意: self.testObject 左值調(diào)用的是set TestObject 方法. 右值為get方法,get 方法比較簡(jiǎn)單不用說(shuō)了而 真接testObject 使用的是成員變量self.testObject = [[testObject alloc] init];
// 錯(cuò) reatin 兩次
(就是說(shuō)涝焙,對(duì)于self..testObject 已經(jīng)是創(chuàng)建對(duì)象并將引入技術(shù)+1卑笨,如果后面繼續(xù)用 [testObject alloc] init]會(huì)造成引入計(jì)數(shù)繼續(xù)+1,所以是2次 )testObject = [NSArray objectbyindex:0];
//錯(cuò) 不安全仑撞,沒(méi)有retain 后面release會(huì)出錯(cuò)
(這里沒(méi)有用 self. 說(shuō)明 不會(huì)調(diào)用 set方法赤兴,所以引入計(jì)數(shù)仍然為 0 ,如果后面 進(jìn)行release操作隧哮, 則會(huì)造成 內(nèi)存訪問(wèn)失斖傲肌)如果testObject已有值也會(huì)memory leak 內(nèi)存泄露
自動(dòng)管理對(duì)象iOS 提供了很多static(+) 創(chuàng)建對(duì)象的類(lèi)方法,這些方面是靜態(tài)的沮翔,可以直接用類(lèi)名調(diào)用如:
NSString *testString = [NSString stringWithFormat:@"test" ];
testString 是自動(dòng)管理的對(duì)象陨帆,你不用relese 他,他有一個(gè)很大的 retain count, release后數(shù)字不變采蚀。
例外有一些通過(guò)alloc 生成的對(duì)象相同是自動(dòng)管理的如:
NSString *testString = [[NSString alloc] initWithString:@"test1"];
retain count 同樣是很大的數(shù)疲牵,沒(méi)辦法release
但為了代碼對(duì)應(yīng),還是應(yīng)該加上[ testString release]; //MRC 中需要不然xcode的Analyze 會(huì)認(rèn)識(shí)內(nèi)存leak, 但I(xiàn)nstruments leak 工具檢測(cè)是沒(méi)有的
第四點(diǎn)(strong 和weak)
iOS 5 中對(duì)屬性的設(shè)置新增了strong 和weak關(guān)鍵字來(lái)修飾屬性(iOS 5 之前不支持ARC)
strong 用來(lái)修飾強(qiáng)引用的屬性搏存;
@property (strong) SomeClass * aObject;
對(duì)應(yīng)原來(lái)的
@property (retain) SomeClass * aObject;
和 @property (copy) SomeClass * aObject;
weak 用來(lái)修飾弱引用的屬性
@property (weak) SomeClass * aObject;
對(duì)應(yīng)原來(lái)的@property (assign) SomeClass * aObject;
第五點(diǎn)(iOS內(nèi)存nil與release的區(qū)別)
nil和release的作用:nil就是把一個(gè)對(duì)象的指針置為空瑰步,只是切斷了指針與內(nèi)存中對(duì)象的聯(lián)系;
而release才是真正通知內(nèi)存釋放這個(gè)對(duì)象璧眠,但是在iOS中其實(shí)也不會(huì)立馬釋放內(nèi)存缩焦,而是將內(nèi)存計(jì)數(shù)器剪去1,直到計(jì)數(shù)器變?yōu)?责静,才會(huì)釋放掉內(nèi)存袁滥,
所以release的目的是為了釋放內(nèi)存,而self.object = nil灾螃,是清空指針题翻。
所以nil并沒(méi)有釋放內(nèi)存,只有release才回真正釋放內(nèi)存。
二者使用的先后順序:如果沒(méi)有release就直接nil嵌赠,那么雖然不會(huì)出錯(cuò)塑荒,卻等于自己制造內(nèi)存泄漏了,因?yàn)閚il之后release就已經(jīng)不起作用了姜挺。相反齿税,如果在使用接口對(duì)象時(shí)只僅僅release沒(méi)有設(shè)置self.myOutlet = nil,那么程序可能也不會(huì)報(bào)錯(cuò)炊豪,但卻會(huì)十分不穩(wěn)定凌箕、不健壯,很容易發(fā)生崩潰現(xiàn)象词渤。因?yàn)橐粋€(gè)接口對(duì)象在release之后牵舱,給它所分配等內(nèi)存就已經(jīng)被釋放了,如果釋放之后系統(tǒng)再用到這個(gè)對(duì)象缺虐,那么程序就會(huì)crash芜壁。如果釋放之后把它的指針置為空,則即便后面的程序用到該對(duì)象高氮,也不會(huì)崩潰沿盅。
strong類(lèi)似于retain
weak類(lèi)似于assign
第六點(diǎn)(copy 和 retain 的區(qū)別)
copy: 建立一個(gè)索引計(jì)數(shù)為1的對(duì)象,然后釋放舊對(duì)象
retain: 釋放舊的對(duì)象纫溃,將舊對(duì)象的值賦予輸入對(duì)象,再提高輸入對(duì)象的索引計(jì)數(shù)為1那上面的是什么該死的意思呢韧掩?
Copy其實(shí)是建立了一個(gè)相同的對(duì)象紊浩,相當(dāng)于是備份,而retain不是:比如一個(gè)NSString對(duì)象疗锐,地址為0×1111坊谁,內(nèi)容為@”STR”Copy到另外一個(gè)NSString之后,地址為0×2222滑臊,內(nèi)容相同口芍,新的對(duì)象retain為1,舊有對(duì)象沒(méi)有變化retain到另外一個(gè)NSString之后雇卷,地址相同(建立一個(gè)指針鬓椭,指針拷貝),內(nèi)容當(dāng)然相同关划,這個(gè)對(duì)象的retain值+1也就是說(shuō)小染,retain是指針拷貝,copy是內(nèi)容拷貝贮折。哇裤翩,比想象的簡(jiǎn)單多了…
第七點(diǎn)(對(duì)autorelease的誤解)autorelease其實(shí)是“延后釋放”
A Cocoa的內(nèi)存管理分為 索引計(jì)數(shù)法(Reference Counting/ Retain Count)和 垃圾收集法(Garbage Collection)。
而iPhone上目前只支持前者调榄,所以autorelease就成為很多人的“捷徑”踊赠。但是呵扛!autorelease其實(shí)并不是“自動(dòng)釋放”,不像垃圾收集法筐带,對(duì)對(duì)象之間的關(guān)系偵測(cè)后發(fā)現(xiàn)垃圾-刪除今穿。
但是autorelease其實(shí)是“延后釋放”,在一個(gè)運(yùn)行周期后被標(biāo)記為autorelease會(huì)被釋放掉烫堤。切記小心使用autorelease荣赶,理解autorelease,防止在你還需要該對(duì)象的時(shí)候已經(jīng)被系統(tǒng)釋放掉了鸽斟。
第八點(diǎn)(其它)
NSArray對(duì)象會(huì)retain(retain值加一+1)任何數(shù)組中的對(duì)象拔创。當(dāng)NSArray被卸載(dealloc)的時(shí)候,所有數(shù)組中的對(duì)象會(huì)被執(zhí)行一次釋放(retain值減一)富蓄。
不僅僅是NSArray剩燥,任何收集類(lèi)(Collection Classes)都執(zhí)行類(lèi)似操作。例如NSDictionary立倍,甚至UINavigationController灭红。Alloc/init建立的對(duì)象,索引計(jì)數(shù)為1口注。無(wú)需將其再次retain变擒。
為什么不能直接調(diào)用dealloc而是release dealloc不等于C中的free,dealloc并不將內(nèi)存釋放寝志,也不會(huì)將索引計(jì)數(shù)(Reference counting)降低娇斑。
于是直接調(diào)用dealloc反而無(wú)法釋放內(nèi)存。
只是當(dāng)當(dāng)前情景下材部,如果創(chuàng)建的所有對(duì)象毫缆、屬性、成員變量等引入技術(shù)均為0 時(shí)乐导,不會(huì)造成內(nèi)存泄露苦丁,就會(huì)調(diào)用 dealloc 方法。
所以可以用以下方法 檢測(cè) iOS 頁(yè)面是否出現(xiàn)內(nèi)存泄露的請(qǐng)款
在Objective-C中物臂,索引計(jì)數(shù)是起決定性作用的旺拉。