前言
從我開始學(xué)習(xí)iOS的時(shí)候,身邊的朋友、網(wǎng)上的博客都告訴我iOS的內(nèi)存管理是依靠引用計(jì)數(shù)的,然后說引用計(jì)數(shù)大于1則對(duì)象保存在內(nèi)存的堆中而引用計(jì)數(shù)等于0則對(duì)象銷毀。然后又說在所謂的ARC時(shí)代,強(qiáng)指針指向一個(gè)對(duì)象,則對(duì)象不銷毀;一個(gè)對(duì)象沒有任何一個(gè)強(qiáng)指針指向則銷毀....,最后,我想說這些都很有道理的樣子,但是,我還是不清楚為什么引用計(jì)數(shù)器為0為什么會(huì)被銷毀,為什么一個(gè)對(duì)象沒有強(qiáng)指針指向就會(huì)銷毀,為什么在@property中一個(gè)OC對(duì)象要使用strong進(jìn)行修飾 .... 。所以,在學(xué)習(xí)Objective-C高級(jí)編程:iOS與OS X多線程和內(nèi)存管理后,讓我明白了很多事情。以下是對(duì)于這本書里面知識(shí)的總結(jié)性內(nèi)容,如果要詳細(xì)了解,請(qǐng)閱讀該書籍。
注意:下面的內(nèi)容是適合于已經(jīng)對(duì)于iOS內(nèi)存管理有一定了解的程序員
內(nèi)存管理的思考方式
自己生成的對(duì)象,自己持有
非自己生成的對(duì)象,自己也能持有
不再需要自己持有對(duì)象時(shí)釋放
非自己持有的對(duì)象無(wú)法釋放
1) 自己生成的對(duì)象,自己持有
在iOS內(nèi)存管理中有四個(gè)關(guān)鍵字,alloc、new、copy、mutableCopy,自身使用這些關(guān)鍵字產(chǎn)生對(duì)象,那么自身就持有了對(duì)象
// 使用了alloc分配了內(nèi)存,obj指向了對(duì)象,該對(duì)象本身引用計(jì)數(shù)為1,不需要retainidobj = [[NSObjectalloc] init];// 使用了new分配了內(nèi)存,objc指向了對(duì)象,該對(duì)象本身引用計(jì)數(shù)為1,不需要retainidobj = [NSObjectnew];
2) 非自己生成的對(duì)象,自己也能持有
// NSMutableArray通過類方法array產(chǎn)生了對(duì)象(并沒有使用alloc、new、copy、mutableCopt來(lái)產(chǎn)生對(duì)象),因此該對(duì)象不屬于obj自身產(chǎn)生的// 因此,需要使用retain方法讓對(duì)象計(jì)數(shù)器+1,從而obj可以持有該對(duì)象(盡管該對(duì)象不是他產(chǎn)生的)idobj = [NSMutableArrayarray];? ? [objretain];
3) 不再需要自己持有對(duì)象時(shí)釋放
idobj = [NSMutableArrayarray];? ? ? [objretain];// 當(dāng)obj不在需要持有的對(duì)象,那么,obj應(yīng)該發(fā)送release消息[obj release];
4) 無(wú)法釋放非自己持有的對(duì)象
// 1. 釋放一個(gè)已經(jīng)釋放的對(duì)象id obj = [[NSObject alloc] init];// 已經(jīng)釋放對(duì)象[objrelease];// 釋放了對(duì)象還進(jìn)行釋放[objrelease];// 2. 釋放一個(gè)不屬于自己的對(duì)象id obj1 = [obj object];// obj1沒有進(jìn)行retain操作而進(jìn)行release操作,使得obj持有對(duì)象釋放,造成了野指針錯(cuò)誤[obj1release];
如上為iOS進(jìn)行內(nèi)存管理的四種思考方式(記住不論是ARC還是MRC都遵循該思考方式,只是ARC時(shí)代這些工作讓編譯器做了)
引用計(jì)數(shù)器討論
蘋果對(duì)于引用計(jì)數(shù)的管理是通過一張引用計(jì)數(shù)表進(jìn)行管理的

引用計(jì)數(shù)表.png
我們平常在操作對(duì)象的引用計(jì)數(shù)器時(shí),其實(shí)就是對(duì)這個(gè)引用計(jì)數(shù)表進(jìn)行操作,在獲取到該表的地址以及相應(yīng)對(duì)象的內(nèi)存地址,就可以通過對(duì)象的內(nèi)存從該表中進(jìn)行索引獲取到相應(yīng)的引用計(jì)數(shù)值,然后根據(jù)用戶的操作來(lái)返回計(jì)時(shí)器、計(jì)時(shí)器加1、計(jì)時(shí)器減1,下面就深入討論retain、release、alloc、dealloc具體怎么操作該引用計(jì)數(shù)表
alloc
當(dāng)我們調(diào)用alloc函數(shù)時(shí)我們進(jìn)一步會(huì)調(diào)用allocWithZone方法
idobj = [[NSObjectalloc] init];? ? + (id)alloc {return[selfallocWithZone:NSDefaultMallocZone()];? ? }? ? + (id)allocWithZone:(NSZone*)z {returnNSAllocateObject(self,0,z);? ? }
調(diào)用NSAllocateObject函數(shù)對(duì)內(nèi)存進(jìn)行分配
retain、release、retainCount
該書籍對(duì)于這三個(gè)函數(shù)調(diào)用先是使用GNUstep(一個(gè)Cocoa框架的互換框架,功能類似)進(jìn)行講解,后來(lái)又講解了蘋果對(duì)于引用計(jì)數(shù)的實(shí)現(xiàn)。在這里我們就討論蘋果的實(shí)現(xiàn)了。
調(diào)用retain、release、retainCount時(shí)函數(shù)調(diào)用順序:

retain、retainCount、release函數(shù)調(diào)用順序.png
如下所示,調(diào)用各個(gè)函數(shù)時(shí)會(huì)調(diào)用__CFDoExternRefOperation函數(shù),該函數(shù)包含于CFRuntime.c中,該函數(shù)簡(jiǎn)化代碼如下:
- (NSUInteger)retainCount {return(NSUInteger)__CFDoExternRefOperation(OPERATION_retainCount,self);}- (id)retain{return(id)__CFDoExternRefOperation(OPERATION_retain,self);}- (void)release {return__CFDoExternRefOperation(OPERATION_release,self);}
int__CFDoExternRefOperation(uintptr_r op,id obj) {? ? ? ? CFBasicHashRef table = 取得對(duì)象對(duì)應(yīng)的散列表(obj);intcount;switch(op) {caseOPERATION_retainCount:count= CFBasicHashGetCountOfKey(table,obj);returncount;caseOPERATION_retain:? ? ? ? ? ? ? ? CFBasicHashAddValue(table,obj);returnobj;caseOPERATION_release:count= CFBasicHashRemoveValue(table,obj):return0==count;? ? ? ? }? ? }
代碼如上所示,可以想象蘋果就是使用類似于上述的引用計(jì)數(shù)表來(lái)管理內(nèi)存,也就是說我們?cè)谡{(diào)用retain、retainCount、release時(shí)首先調(diào)用__CFDoExternRefOperation進(jìn)而獲取到引用技術(shù)表的內(nèi)存地址以及本對(duì)象的內(nèi)存地址,然后根據(jù)對(duì)象的內(nèi)存地址在表中查詢獲取到引用計(jì)數(shù)值。
若是retain就加1
若是retainCount就直接返回值,
若是release則減1而且在CFBasicHashRemoveValue中將引用計(jì)數(shù)減少到0時(shí)會(huì)調(diào)用dealloc,從而調(diào)用NDDeallocateObject函數(shù)、free函數(shù)將對(duì)象所在內(nèi)存釋放
以上就是在討論蘋果對(duì)于引用計(jì)數(shù)的管理方法,對(duì)于GNUStep辦法請(qǐng)自行查閱書籍
autorelease
作用:將對(duì)象放入自動(dòng)釋放池中,當(dāng)自從釋放池銷毀時(shí)對(duì)自動(dòng)釋放池中的對(duì)象都進(jìn)行一次release操作
書寫形式:
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc]init];? ? id obj =[[NSObject alloc]init];[obj autorelease];[pool drain];
對(duì)于autorelease的實(shí)現(xiàn)方式,書籍也對(duì)比了GNUSetp與蘋果實(shí)現(xiàn)的方式,現(xiàn)在通過GNUStep源代碼來(lái)理解蘋果的實(shí)現(xiàn)
1) GNUStep實(shí)現(xiàn)
id obj =[[NSObject alloc]init];[obj autorelease];
- (id)autorelease {? ? ? ? [NSAutoreleasePooladdObject:self];? ? }
+ (void)addObject:(id)anObject {NSAutoreleasePool*pool = 取得正在使用的Pool對(duì)象;if(pool !=nil) {? ? ? ? ? ? [pool addObject:anObject];? ? ? ? }else{NSLog(@"NSAutoreleasePool非存在狀態(tài)下使用Pool對(duì)象");? ? ? ? }? ? }
- (void)addObject:(id)anObject {? ? ? ? [arrayaddObject:anObject];? ? }
從上面可以看出,自動(dòng)釋放池就是通過數(shù)組完成的,我們?cè)谡{(diào)用autorelease時(shí)最終就是將本對(duì)象添加到當(dāng)前自動(dòng)釋放池的數(shù)組
而針對(duì)于自動(dòng)釋放池銷毀時(shí)對(duì)數(shù)組中的進(jìn)行一次release操作,見下面
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc]init];? ? ...? ? // 當(dāng)自動(dòng)釋放池銷毀時(shí)[pool drain];
- (void)drain {? ? ? ? [self dealloc];? ? }? ? - (void)dealloc {? ? ? ? [self emptyPool];? ? ? ? [arrayrelease];? ? }? ? - (void)emptyPool {for(id obj inarray) {? ? ? ? ? ? [objrelease];? ? ? ? }? ? }
2) 蘋果的實(shí)現(xiàn)
classAutoreleasePoolPage? ? {staticinlinevoid*push(){? ? ? ? ? ? 相當(dāng)于生成或持有NSAutoreleasePool類對(duì)象? ? ? ? }staticinlinevoid*pop(void*token){? ? ? ? ? ? 相當(dāng)于廢棄NSAutoreleasePool類對(duì)象? ? ? ? ? ? releaseAll();? ? ? ? }staticinlineidautorelease(id obj){? ? ? ? ? ? 相當(dāng)于NSAutoreleasePool類的addObject類方法? ? ? ? ? ? ? AutoreleasePoolPage *autoreleasePoolPage = 取得正在使用的AutoreleasePoolPage實(shí)例;? ? ? ? ? ? autoreleasePoolPage->add(obj);? ? ? ? }id *add(id obj){? ? ? ? ? ? 將對(duì)象追加到內(nèi)部數(shù)組中? ? ? ? }voidreleaseAll(){? ? ? ? ? ? 調(diào)用內(nèi)部數(shù)組中對(duì)象的release實(shí)例方法? ? ? ? }? ? };void*objc_autoreleasePoolPush(void){returnAutoreleasePoolPage::push();? ? }voidobjc_autoreleasePoolPage(void*ctxt){? ? ? ? AutoreleasePoolPage::pop(ctxt);? ? }id *objc_autorelease(id obj){returnAutoreleasePoolPage::autorelease(obj);? ? }
如上所示,蘋果內(nèi)部使用了類似于GNUStep中的思想,將對(duì)象添加進(jìn)數(shù)組進(jìn)行管理
ARC中內(nèi)存管理方式
介紹:
關(guān)于這部分的內(nèi)存,作者是分了兩部分進(jìn)行討論,第一部分介紹ARC管理所需要的關(guān)鍵字strong 、weak、unsafe_unretained、autoreleasing的作用;第二部分介紹了ARC針對(duì)于這些關(guān)
鍵字的具體內(nèi)管管理實(shí)現(xiàn)方式。下面我們就綜合兩部分的內(nèi)容進(jìn)行一次討論
蘋果官方文檔說ARC是有"編譯器自行進(jìn)行管理",但事實(shí)上僅僅是編譯器是不夠,需要滿足下面啷個(gè)條件
clang(LLVM編譯器)3.0以上
objc4 Objective-C運(yùn)行時(shí)庫(kù)493.9以上
__strong
作用
id__strongobj = [[NSObjectalloc]init];
如上代碼,表示obj這個(gè)強(qiáng)指針指向NSObject對(duì)象,且NSObject對(duì)象的引用計(jì)數(shù)為1
id__strongobj1 = obj;
如上代碼,表示obj1這個(gè)強(qiáng)指針與obj指針指向同一個(gè)NSObject對(duì)象,且NSObject對(duì)象的引用計(jì)數(shù)為2
id__strongobj = [NSMutableArrayarray];
如上代碼,表示obj這個(gè)強(qiáng)指針指向的NSMutableArray對(duì)象的引用計(jì)數(shù)為1
綜上所示,當(dāng)一個(gè)對(duì)象被強(qiáng)指針指向則引用計(jì)數(shù)就加1,否則,該對(duì)象沒有一個(gè)強(qiáng)指針指向則自動(dòng)釋放內(nèi)存
那么問題來(lái)了,為什么一個(gè)對(duì)象被強(qiáng)指針指向引用計(jì)數(shù)就加1呢? 為什么分配在堆里面的對(duì)象內(nèi)存能夠自動(dòng)釋放內(nèi)存?
原理
第一種情況: 對(duì)象是通過alloc、new、copy、multyCopy來(lái)分配內(nèi)存的
id__strongobj = [[NSObjectalloc] init];
當(dāng)使用alloc、new、copy、multyCopt進(jìn)行對(duì)象內(nèi)存分配時(shí),強(qiáng)指針直接指向一個(gè)引用計(jì)數(shù)為1的對(duì)象,在編譯器作用下,上述代碼會(huì)轉(zhuǎn)換成以下代碼
id obj = objc_msgSend(NSObject,@selector(alloc));? ? objc_msgSend(obj,@selector(init));// 當(dāng)讓這個(gè)代碼會(huì)在合適的時(shí)候被調(diào)用,不是馬上調(diào)用objc_release(obj);
第二種情況: 對(duì)象不是自身生成,但是自身持有(一般這樣的對(duì)象是通過除alloc、new、copy、multyCopy外方法產(chǎn)生的)
id__strongobj = [NSMutableArrayarray];
在這種情況下,obj也指向一個(gè)引用計(jì)數(shù)為1的對(duì)象內(nèi)存,其在編譯器下轉(zhuǎn)換的代碼如下:
idobj = objc_msgSend(NSMutableArray,@selector(array));// 代替我們調(diào)用retain方法,使得obj可以持有該對(duì)象objc_retainAutoreleasedReturnValue(obj);? ? objc_release(obj);
從而使得obj指向了一個(gè)引用計(jì)數(shù)為1的對(duì)象, 不過,objc_retainAutoreleaseReturnValue有一個(gè)成對(duì)的函數(shù)objc_autoreleaseReturnValue,這兩個(gè)函數(shù)可以用于最優(yōu)化程序的運(yùn)行
如下代碼:
+ (id)array? ? {return[[NSMutableArrayalloc] init];? ? }
代碼轉(zhuǎn)換如下:
+ (id)array? ? {? ? ? ? id obj = objc_msgSend(NSMutableArray,@selector(alloc));? ? ? ? objc_msgSend(obj,@selector(init));// 代替我們調(diào)用了autorelease方法returnobjc_autoreleaseReturnValue(obj);? ? }
在轉(zhuǎn)換后的代碼,我們可以看見調(diào)用了objc_autoreleaseReturnValue函數(shù)且這個(gè)函數(shù)會(huì)返回注冊(cè)到自動(dòng)釋放池的對(duì)象,但是,這個(gè)函數(shù)有個(gè)特點(diǎn),它會(huì)查看調(diào)用方的命令執(zhí)行列表,如果發(fā)現(xiàn)接
下來(lái)會(huì)調(diào)用objc_retainAutoreleasedReturnValue則不會(huì)返回注冊(cè)到自動(dòng)釋放池的對(duì)象而僅僅返回一個(gè)對(duì)象而已。
兩者的關(guān)系圖如下:

關(guān)系圖.png
通過這些,我們就可以通知為什么強(qiáng)指針指向一個(gè)對(duì)象,這個(gè)對(duì)象的引用計(jì)數(shù)就加1
__weak
作用
id__weakobj = [[NSObjectalloc] init];
根據(jù)我們的知識(shí),可以知道NSObject對(duì)象在生成之后立馬就會(huì)被釋放,其主要原因是weak修飾的指針沒有引起對(duì)象內(nèi)部的引用計(jì)數(shù)器的變化
因此,weak修飾的指針常用于打破循環(huán)引用或者修飾UI控件,關(guān)于__weak修飾的指針引用場(chǎng)景這里不敘述,下面主要介紹其原理
原理
我們知道弱指針有兩個(gè)作用:一. 修飾的指針不會(huì)引起指向的對(duì)象的引用計(jì)數(shù)器變化 二. 當(dāng)指向的對(duì)象被銷毀時(shí),弱指針全部置為nil, 那么除了這些之外,我們還有一個(gè)要說的就是,為什么我們
在程序中不能頻繁的使用weak呢?
1) 為什么弱指針不會(huì)引起指向的對(duì)象的引用計(jì)數(shù)器發(fā)生變化
id__weakobj = [[NSObjectalloc] init];
編譯器轉(zhuǎn)換后的代碼如下:
id obj;id tmp = objc_msgSend(NSObject,@selector(alloc));objc_msgSend(tmp,@selector(init));objc_initweak(&obj,tmp);objc_release(tmp);objc_destroyWeak(&object);
對(duì)于__weak內(nèi)存管理也借助了類似于引用計(jì)數(shù)表的表,它通過對(duì)象的內(nèi)存地址做為key,而對(duì)應(yīng)的指針作為value進(jìn)行管理,在上述代碼中objc_initweak就是完成這部分操作,而objc_destroyWeak
則是銷毀該對(duì)象對(duì)應(yīng)的value。所以,weak在修飾只是讓weak表增加了記錄沒有引起引用計(jì)數(shù)表的變化
2) 當(dāng)弱指針指向的對(duì)象唄銷毀時(shí),弱指針怎么才能自動(dòng)置為nil? 為什么我們?cè)诔绦蛑胁荒茴l繁使用weak呢
對(duì)象通過objc_release釋放對(duì)象內(nèi)存的動(dòng)作如下:
objc_release
因?yàn)橐糜?jì)數(shù)為0所以執(zhí)行dealloc
_objc_rootDealloc
objc_dispose
objc_destructInstance
objc_clear_deallocating
而在對(duì)象被廢棄時(shí)最后調(diào)用了objc_clear_deallocating,該函數(shù)的動(dòng)作如下:
1) 從weak表中獲取已廢棄對(duì)象內(nèi)存地址對(duì)應(yīng)的所有記錄
2)將已廢棄對(duì)象內(nèi)存地址對(duì)應(yīng)的記錄中所有以weak修飾的變量都置為nil
3)從weak表刪除已廢棄對(duì)象內(nèi)存地址對(duì)應(yīng)的記錄
4)根據(jù)已廢棄對(duì)象內(nèi)存地址從引用計(jì)數(shù)表中找到對(duì)應(yīng)記錄刪除
據(jù)此可以解釋為什么對(duì)象被銷毀時(shí)對(duì)應(yīng)的weak指針變量全部都置為nil,同時(shí),也看出來(lái)銷毀weak步驟較多,如果大量使用weak的話會(huì)增加CPU的負(fù)荷
而不建議大量使用weak,還有一個(gè)原因看下面的代碼:
id__weakobj1 = obj;NSLog(@"obj2-%@",obj1);
編譯器轉(zhuǎn)換上述代碼如下:
id obj1;? ? objc_initweak(&obj1,obj);// 從weak表中獲取附有__weak修飾符變量所引用的對(duì)象并retainid tmp = objc_loadWeakRetained(&obj1);// 將對(duì)象放入自動(dòng)釋放池objc_autorelease(tmp);? ? NSLog(@"%@",tmp);? ? objc_destroyWeak(&obj1);
據(jù)此當(dāng)我們?cè)L問weak修飾指針指向的對(duì)象時(shí),實(shí)際上是訪問注冊(cè)到自動(dòng)釋放池的對(duì)象。因此,如果大量使用weak的話,在我們?nèi)ピL問weak修飾的對(duì)象時(shí),會(huì)有大量對(duì)象注冊(cè)到自動(dòng)釋放池,這會(huì)影響程
序的性能。推薦方案 : 要訪問weak修飾的變量時(shí),先將其賦給一個(gè)strong變量,然后進(jìn)行訪問
最后一個(gè)問題: 為什么訪問weak修飾的對(duì)象就會(huì)訪問注冊(cè)到自動(dòng)釋放池的對(duì)象呢?
因?yàn)閣eak不會(huì)引起對(duì)象的引用計(jì)數(shù)器變化,因此,該對(duì)象在運(yùn)行過程中很有可能會(huì)被釋放。所以,需要將對(duì)象注冊(cè)到自動(dòng)釋放池中并在自動(dòng)釋放池銷毀時(shí)釋放對(duì)象占用的內(nèi)存。
__unsafe_unretained
作用
unsafe_unretained作用需要和weak進(jìn)行對(duì)比,它也不會(huì)引起對(duì)象的內(nèi)部引用計(jì)數(shù)器的變化,但是,當(dāng)其指向的對(duì)象被銷毀時(shí)unsafr_unretained修飾的指針不會(huì)置為nil。而且一般__unsafe_unretained就和它的名字一樣是不安全,它不納入ARC的內(nèi)存管理
__autoreleasing
作用
ARC無(wú)效
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc]init];? ? id obj =[[NSObject alloc]init];[obj autorelease];[pool drain];
ARC有效*
id __autoreleasing obj1 = obj;
如上所示,通過__autoreleasing修飾符就完成了ARC無(wú)效時(shí)一樣的功能
當(dāng)然,在某一些情況下我們不通過顯式指定__autoreleasing關(guān)鍵字就可以完成自動(dòng)注冊(cè)到自動(dòng)釋放池的功能,例如以下情況
第一種:
@autoeleasepool {// 如果看了上面__strong的原理,就知道實(shí)際上對(duì)象已經(jīng)注冊(cè)到自動(dòng)釋放池里面了id__strongobj = [NSMutableArrayarray];? ? }
第二種:
訪問__weak修飾的對(duì)象時(shí),對(duì)象就被注冊(cè)到了自動(dòng)釋放池
第三種:
以下形式的默認(rèn)修飾符是__autorelease
id *obj;
NSObject **obj;
同時(shí),也引出一個(gè)問題: 為什么在@property中OC對(duì)象使用strong而基本數(shù)據(jù)類型使用assign?

屬性默認(rèn)修飾符.png
從表中可以推斷出,在ARC在OC對(duì)象的默認(rèn)修飾符是strong,因此,在@property中使用strong
而基本數(shù)據(jù)類型是不納入到ARC內(nèi)存管理中的,unsafe_unretained也不歸ARC管,因此,使用assign對(duì)基本數(shù)據(jù)類型進(jìn)行修飾
原理 ```objc @autoreleasepool {
id __autoreleasing obj =[[NSObject alloc]init];}
代碼轉(zhuǎn)換如下:? ```objc? ? id pool = objc_autoreleasePoolPush();id obj = objc_msgSend(NSObject,@selector(alloc));objc_msgSend(obj,@selector(init));objc_autorelease(obj);objc_autoreleasePoolPop(pool);
@autoreleasepool{id__autoreleasing obj = [NSMutableArrayarray];? ? }
代碼轉(zhuǎn)換如下:
id pool = objc_autoreleasePoolPush();id obj = objc_msgSend(NSMutableArray,@selector(array));objc_retainAutoreleasedReturnValue(obj);objc_autorelease(obk);objc_autoreleasePoolPop(pool);
上述代碼,代表的就是自身生成并持有對(duì)象、自身不生成但也持有對(duì)象的兩種__autorelease內(nèi)存管理情況
ARC規(guī)則
不能使用retain、release、retainCount、autorelease方法(如果ARC下使用會(huì)出現(xiàn)編譯錯(cuò)誤)
不能使用NSAllocateObject、NSDeallocateObject函數(shù)(如果ARC下使用會(huì)出現(xiàn)編譯錯(cuò)誤)
不要顯式調(diào)用dealloc(ARC下,顯式調(diào)用dealloc并在代碼中書寫[super dealloc]也會(huì)出現(xiàn)編譯錯(cuò)誤)
使用@autoreleasepool塊代替NSAutoreleasePool
@autoreleasepool{}塊相比較NSAutoreleasePool而言顯得代碼更加整潔、層次性強(qiáng),而且@autoreleasepool代碼快哉ARC或者非ARC下都是可以使用的
需遵守內(nèi)存管理命名規(guī)則
1) alloc、new、copy、mutableCopy等以這些名字開頭的方法都應(yīng)當(dāng)返回調(diào)用方能夠持有的對(duì)象2)init開頭的方法必須是實(shí)例方法并且要返回對(duì)象,返回值要是id或者該方法對(duì)應(yīng)類的對(duì)象類似或者其超類或者其子類。另外,init開頭的方法也僅僅用作對(duì)對(duì)象進(jìn)行初始化操作
不能使用區(qū)域(NSZone)
區(qū)域是以前為了高效利用內(nèi)存的使用率而設(shè)計(jì)的,但是,目前來(lái)說ARC下的模式已經(jīng)能夠有效利用內(nèi)存,區(qū)域在ARC下還是非ARC下都已經(jīng)被單純的忽略
對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體的成員
OC對(duì)象型變量如果成為了C語(yǔ)言結(jié)構(gòu)體的成員,那么,ARC不能掌握該對(duì)象的生命周期從而有效管理內(nèi)存,因此,不能這樣使用。
顯式轉(zhuǎn)換"id" 和 "void*"
非ARC下:idobj = [[NSObjectalloc] init];void*p = obj;? ? 這樣的代碼是可行的,id和void*可以方便得自由轉(zhuǎn)化 ,但是,在ARC下是不一樣的? ? ARC下id和void*有三個(gè)轉(zhuǎn)換的關(guān)鍵字 __bridge、__bridge_retained、__bridge_transfer:idobj = [[NSObjectalloc] init];void*p = (__bridgevoid*)obj;? ? 注意: __bridge不會(huì)引起對(duì)象的引用計(jì)數(shù)變化,因此,安全性不太好。相比較,__bridge_retained不僅僅實(shí)現(xiàn)了__bridge的功能而且能讓p調(diào)用retain方法使p持有對(duì)象。另外,? ? __bridge_transfer也是和release方法類似,使用__bridge_transfer進(jìn)行轉(zhuǎn)化,既讓對(duì)象p調(diào)用一次retain方法,