現(xiàn)在大家的項(xiàng)目應(yīng)該基本都是ARC了,如果還是MRC的話奈虾,趕緊轉(zhuǎn)換到ARC吧肆汹!最近被臨時(shí)拉過去開發(fā)iPad,由于項(xiàng)目原因昂勉,還是使用的MRC。今天在調(diào)部分界面的時(shí)候村象,發(fā)現(xiàn)一段代碼攒至,我怎么看都怎么覺得怪怪的,因?yàn)槭荕RC嘛迫吐!所以我心里還是一直提醒著自己。仔細(xì)一看還真是不對(duì)熙宇,這段代碼給周圍同事看的時(shí)候,也不是每個(gè)人都能一眼看出問題烫止,因?yàn)榇蠹乙呀?jīng)習(xí)慣了ARC或者沒有在MRC下進(jìn)行開發(fā)過。
下面我貼出類似的代碼:
- (void)pushVc
{
pushVC = [[UIViewController alloc] init];
[self.navigationController pushViewController:pushVC animated:YES];
}
以上這段代碼很簡單期升,就是有個(gè)UIViewController類型的成員變量pushVC
互躬,然后創(chuàng)建一個(gè)VC賦值給他,最后push到這個(gè)頁面行拢〉ǎ可能很多人一看竭缝,這代碼就是平常自己寫的啊,都沒有出現(xiàn)過問題啊抬纸。如果這段代碼是在ARC下,是沒有任何問題的阿趁。但是坛猪,如果我們的代碼是在MRC下,會(huì)出現(xiàn)什么問題呢命黔?如果經(jīng)歷過MRC開發(fā)的人就斤,肯定也會(huì)覺得這邊怪怪的。至少?zèng)]有發(fā)現(xiàn)調(diào)用release
洋机。由于pushVC
是成員變量,所以一定程度上也迷惑了下同事喜鼓。上面的代碼其實(shí)已經(jīng)內(nèi)存泄露了。[[UIViewController alloc] init]
這個(gè)方法創(chuàng)建出來的對(duì)象將不會(huì)被銷毀颠通,一直留在內(nèi)存中。為什么谨垃?這個(gè)對(duì)象創(chuàng)建出來的時(shí)候引用是1硼控,然后經(jīng)過push
引用計(jì)數(shù)已經(jīng)變成2了。當(dāng)這個(gè)vc在后面被pop
出來的時(shí)候牢撼,引用計(jì)數(shù)會(huì)減1,這時(shí)這個(gè)VC的引用計(jì)數(shù)還是1纷责。在內(nèi)存中將銷毀不掉撼短。如果這個(gè)方法被多次調(diào)用的話,將會(huì)出現(xiàn)大量的這個(gè)對(duì)象在內(nèi)存中喂柒。
下面再說一個(gè)知識(shí)點(diǎn)禾嫉,很多人知道,但是并不一定完全了解我們的@property
到底做了什么熙参。
- (void)pushVc
{
self.pushVC = [[UIViewController alloc] init];
[self.navigationController pushViewController:pushVC animated:YES];
}
在看這段代碼,我給成員變量賦值的方式換成了self.pushVC
,這個(gè)和直接賦值有什么區(qū)別呢讲竿?如果你調(diào)用self.pushVC
進(jìn)行賦值弄屡,那么這個(gè)時(shí)候會(huì)調(diào)用系統(tǒng)為我們默認(rèn)生成的setter
方法。這個(gè)setter會(huì)幫我們做內(nèi)存的引用計(jì)數(shù)操作膀捷。看下系統(tǒng)生成的方法示例:
- (void)setPushVC:(UIViewController *)setPushVC
{
[setPushVC retain];
[pushVC release];
pushVC = setPushVC;
}
首先秀仲,系統(tǒng)會(huì)將傳進(jìn)來的對(duì)象引用計(jì)數(shù)加1,之后將賦值的對(duì)象引用計(jì)數(shù)減1雁刷,最后再給對(duì)象賦值保礼。記得自己重寫setter
方法的時(shí)候,一定要先將傳進(jìn)來的對(duì)象做下retain
操作炮障,之后在release
本身的對(duì)象。如果你代碼這樣寫的話:
- (void)setPushVC:(UIViewController *)setPushVC
{
[pushVC release];
[setPushVC retain];
pushVC = setPushVC;
}
正常情況下是沒有問題的企蹭,但是如果是自己給自己賦值的話self.pushVC = pushVC
智末,那就有問題了。當(dāng)然你可以做下if判斷螟凭,兩個(gè)對(duì)象是否一樣它呀,那樣也行棒厘。
接下來看下這個(gè)代碼的正確寫法:
UIViewController *VC = [[UIViewController alloc] init];
pushVC = [VC retain];
[VC release];
[self.navigationController pushViewController:VC animated:YES];
//或者
UIViewController *VC = [[UIViewController alloc] init];
self.pushVC = VC
[VC release];
[self.navigationController pushViewController:VC animated:YES];
建議大家在MRC下使用成員變量的時(shí)候最好使用self.
setter方法。有同事又提出了另一種寫法:
pushVC = [[[UIViewController alloc] init] autorelease];
[self.navigationController pushViewController:pushVC animated:YES];
用autorelease
,但是這樣寫有個(gè)問題谓媒,一旦你使用這個(gè)關(guān)鍵字何乎,那你就不在有這個(gè)創(chuàng)建對(duì)象的內(nèi)存管理權(quán),系統(tǒng)會(huì)在之后的某個(gè)時(shí)間支救,對(duì)其進(jìn)行release
操作。這樣也違背了用成員變量保存這個(gè)VC的意圖指孤。
總結(jié)
很多同事一眼沒有看出來,是因?yàn)槲覀円呀?jīng)習(xí)慣了ARC恃轩,認(rèn)為=
就是給對(duì)象進(jìn)行了retain
。在ARC下默認(rèn)變量前面都有一個(gè)隱藏的__strong
松忍。在ARC下只要變量指向?qū)ο罂昀澹敲聪到y(tǒng)會(huì)我們自動(dòng)的對(duì)那個(gè)對(duì)象進(jìn)行retain
操作,當(dāng)我們將對(duì)象置為nil
的時(shí)候叽掘,系統(tǒng)會(huì)默認(rèn)給我們做release
操作玖雁。
引用計(jì)數(shù)內(nèi)存管理的思考方式:
- 自己生成的對(duì)象,自己所持有
- 非自己生成的對(duì)象赫冬,自己也恩能持有
- 自己持有的對(duì)象不再需要時(shí)釋放
- 非自己持有的對(duì)象無法釋放
當(dāng)我們使用ARC的時(shí)候,也是遵循了上面的思考方式膛薛。不要因?yàn)槲覀儧]有看到retain
或者release
而認(rèn)為管理方式變了或者不需要內(nèi)存管理了补鼻。ARC看起來很簡單,因?yàn)樘O果把那些引用計(jì)數(shù)的操作代碼都交給了編譯器咨跌,所以給了我們這種錯(cuò)覺硼婿。了解MRC,可以加深自己對(duì)ARC的理解寇漫。不至于讓自己被ARC給蒙蔽了。
使用ARC可以讓我們的代碼更加精簡记焊,健壯栓撞,特別是weak
這個(gè)關(guān)鍵字,更是解決了野指針的問題。