Objective-C學(xué)習(xí)之ARC的實(shí)現(xiàn)方法
前言
在ARC出現(xiàn)以前,程序員們只能靠retain/relese/autorelease來確保對(duì)象們恰好“堅(jiān)持”到被需要的那一刻。如果忘了retain,或者多次release某個(gè)對(duì)象,程序就會(huì)發(fā)生內(nèi)存泄漏的問題,甚至直接崩潰。
在Xcode 4.2中,除了語法檢查外,Apple的新LLVM編譯器還將內(nèi)存管理的苦差事接了過來,它會(huì)檢查代碼,決定何時(shí)釋放對(duì)象。Apple的文檔里是這么定義ARC的:
“自動(dòng)引用計(jì)數(shù)(ARC)是一個(gè)編譯器級(jí)的功能,它能簡(jiǎn)化Cocoa應(yīng)用中對(duì)象生命周期管理(內(nèi)存管理)的流程?!?/p>
ARC使內(nèi)存管理在大部分時(shí)候變得如同小事一樁,但我們?nèi)砸跊Q定自己的類如何管理其它對(duì)象的引用時(shí)承擔(dān)一些責(zé)任。
蘋果的官方說明中稱,ARC是“由編譯器進(jìn)行內(nèi)存管理”的,但是實(shí)際上只有編譯其是無法完全勝任的,再次基礎(chǔ)上還需要Objective-C運(yùn)行時(shí)庫的協(xié)助。
也就是說,ARC由以下工具、庫來實(shí)現(xiàn)。
- clang(LLVM編輯器)
- objc4 Objective-C 運(yùn)行時(shí)庫
__strong 修飾符
{ id __strong obj = [[NSObject alloc] init]; }
~~本人是c/cpp小白,沒有編譯成功,沒看到匯編輸出TAT~~
以上代碼編譯器的模擬代碼:
/** 編譯器的模擬代碼 */ id obj = objc_msgSend(NSObject, @selector(alloc)); objc_msgSend(obj,@selector(init)); objc_release(obj);
如上所示,調(diào)用了2次objc_msgSend方法,變量的作用域結(jié)束時(shí)通過objc_release釋放對(duì)象。雖然ARC有效時(shí)不能使用release方法,但由此可知編譯器自動(dòng)插入了release。
使用alloc/new/copy/mutableCopy以外的方法時(shí):
{ id __strong obj = [NSMutableArray array]; }
編譯器的模擬代碼如下:
/** 編譯器的模擬代碼 */ id obj = objc_msgSend(NSMutableArray,@selector(array)); objc_retainAutoreleasedReturnValue(obj); objc_release(obj);
其中的objc_retainAutoreleasedReturnValue函數(shù)主要用于優(yōu)化程序運(yùn)行,它用于自己持有(retain)對(duì)象的函數(shù),但它持有的對(duì)象應(yīng)為返回注冊(cè)在autoreleasepool中對(duì)象的方法,或是函數(shù)的返回值。在調(diào)用alloc/new/copy/mutableCopy以外的方法,由編譯器插入該函數(shù)。
與 objc_retainAutoreleaseReturnValue函數(shù)相對(duì)的函數(shù)為objc_autoreleaseReturnValue函數(shù)。它用于alloc/new/copy/mutableCopy方法以外的NSMutableArray類的array類方法等返回對(duì)象的實(shí)現(xiàn)上。
+ (id) array { return [[NSArray alloc] init]; }
轉(zhuǎn)換后
+ (id) array { id obj = objc_msgSend(NSArray,@selector(alloc)); objc_msgSend(obj,@selector(init)); return objc_autoreleaseReturnValue(obj); }
返回注冊(cè)到autoreleasepool中對(duì)象的方法使用了objc_autoreleaseReturValue函數(shù)返回注冊(cè)到autoreleasepool中的對(duì)象。但是objc_autoreleaseReturValue函數(shù)同objc_autorelease函數(shù)不同,一般不僅限于注冊(cè)對(duì)象到autoreleasepool中。
objc_autoreleaseReturValue函數(shù)會(huì)檢查使用該函數(shù)的方法或函數(shù)調(diào)用方的執(zhí)行命令列表。如果方法或函數(shù)的調(diào)用方在調(diào)用了方法或函數(shù)后緊接著調(diào)用objc_retainAutoreleasedReturnValue()函數(shù),那么就不將返回的對(duì)象注冊(cè)到autoreleasepool中,而直接傳遞到方法或函數(shù)的調(diào)用方。objc_retainAutoreleasedReturnValue函數(shù)與objc_retain函數(shù)不同,它即便不注冊(cè)到autoreleasepool中而返回對(duì)象,也能夠正確地獲取對(duì)象。
通過objc_autoreleaseReturnValue函數(shù)和objc_retainAutoreleasedReturnValue函數(shù)的協(xié)作,可以不講對(duì)象注冊(cè)到autoreleasepool中而直接傳遞,這一過程達(dá)到了最優(yōu)化。
__weak 修飾符
- 若附有__weak修飾符的變量所引用的對(duì)象被廢棄,則將nil賦值給該變量。
- 使用附有__weak修飾符的變量,即是使用注冊(cè)到了autoreleasepool中的對(duì)象。
{ id __weak obj1 = obj; }
/** 編譯器的模擬代碼 */ id obj1; objc_initWeak(&obj1,obj); objc_destroyWeak(&obj1);
通過objc_initWeak函數(shù)初始化附有__weak修飾符的變量,在變量作用域結(jié)束時(shí)通過objc_destroyWeak函數(shù)釋放該變量。
如以下源碼所示,objc_initWeak函數(shù)將附有__weak修飾符的變量初始化為0后,會(huì)將賦值的對(duì)象作為參數(shù)調(diào)用objc_storeWeak函數(shù)。
obj1 = 0; objc_storeWeak(&obj1,obj);
objc_destroyWeak函數(shù)將0作為參數(shù)調(diào)用objc_storeWeak函數(shù)。
objc_storeWeak(&obj1,0);
即前面的源代碼和以下代碼相同:
id obj1; obj1 = 0; objc_storeWeak(&obj1,obj); objc_storeWeak(&obj1,0);
objc_weakStore函數(shù)把第二參數(shù)的復(fù)制對(duì)象的地址作為鍵值,將第一參數(shù)的附有__weak修飾符的變量的地址注冊(cè)到weak表中,如果第二參數(shù)為0,則把變量的地址從weak表中刪除。
weak表與引用計(jì)數(shù)表相同,作為散列表被實(shí)現(xiàn)。如果使用weak表,將廢棄對(duì)象的地址作為鍵值進(jìn)行檢索,能高速地獲取對(duì)應(yīng)的附有__weak修飾符的變量的地址。另外,由于一個(gè)對(duì)象可以同時(shí)賦值給多個(gè)附有__weak修飾符的變量中,所以對(duì)于一個(gè)鍵值,可注冊(cè)多個(gè)變量的地址。
釋放對(duì)象時(shí),廢棄誰都不持有的對(duì)象,通過objc_release函數(shù)釋放。
- objc_release
- 因?yàn)橛?jì)數(shù)為0,所以執(zhí)行dealloc
- _objc_rootDealloc
- object_dispose
- objc_destructInstanse
- objc_clear_deallocating
對(duì)象被廢棄時(shí)最后調(diào)用的objc_clear_deallocating函數(shù)動(dòng)作如下:
- 從weak表中獲取廢棄對(duì)象的地址為鍵值的記錄。
- 將包含在記錄中的所有附有__weak修飾符變量的地址,賦值為nil
- 從weak表中刪除該記錄。
- 從引用計(jì)數(shù)表中刪除廢棄對(duì)象的地址作為鍵值的記錄。
根據(jù)以上步驟,前面說的如果附有__weak修飾符的變量所引用的對(duì)象被廢棄,則將nil賦值給該變量這一功能即被實(shí)現(xiàn)。由此可知,如果大量使用附有__weak修飾符的變量,則會(huì)消耗相應(yīng)的CPU資源,對(duì)此只在需要避免循環(huán)引用的時(shí)候使用__weak修飾符。
使用__weak修飾符時(shí),以下代碼會(huì)引起編譯器警告
{ id __weak obj = [[NSObject alloc] init]; NSLog(@"obj = %@",obj); }
編譯結(jié)果如下:
Assigning retained object to weak variable; object will be released after assignment
編譯器模擬代碼如下:
id obj; id temp = objc_msgSend(NSObject,@selector(alloc)); objc_msgSend(temp,@selector(init)); objc_initWeak(&obj,temp); objc_release(temp); objc_destroyWeak(&obj);
運(yùn)行結(jié)果如下:
2017-12-07 19:37:24.075939+0800 ImageOrientation[10963:3581164] obj = (null)
使用附有__weak修飾符的變量,即是使用注冊(cè)到autoreleasepool中的對(duì)象。
{ id __weak obj1 = obj; NSLog(@"%@",obj1); }
該代碼可以轉(zhuǎn)換為如下形式:
/** 編譯器模擬代碼*/ id obj1; objc_initWeak(&obj1,obj); id temp = objc_loadWeakRetained(&obj1); objc_autorelease(temp); NSLog(@"%@",obj1); objc_destroyWeak(&obj1);
與賦值時(shí)相比,在使用附有__weak修飾符變量的情形下,增加了對(duì)objc_loadWeakRetained函數(shù)和objc_autorelease函數(shù)的調(diào)用。這些函數(shù)的動(dòng)作如下:
- objc_loadWeakRetained函數(shù)取出附有__weak修飾符變量所引用的對(duì)象并retain
- objc_autorelease函數(shù)將對(duì)象注冊(cè)到autoreleasepool中。
__autoreleasing 修飾符
將對(duì)象賦值給附有__autoreleasing修飾符的變量等同于MRC時(shí)調(diào)用對(duì)象的autorelease方法。
@autoreleasepool{ id __autoreleasing obj = [[NSObject alloc] init]; }
模擬代碼如下:
/** 編譯器的模擬代碼 */ id pool = objc_autoreleasePoolPush(); id obj = objc_msgSend(NSObject,@selector(alloc)); objc_msgSend(obj,@selector(init)); objc_autoreleas(obj); objc_autoreleasePoolPop(pool);
alloc/new/copy/mutableCopy之外的方法實(shí)現(xiàn):
@autoreleasepool{ id __autoreleasing obj = [NSMutableArray array]; }
/** 編譯器的模擬代碼 */ id pool = objc_autoreleasePoolPush(); id obj = objc_msgSend(NSMutableArray,@selector(array)); objc_retainAutorelesedReturnedValue(obj); objc_autorelease(obj); objc_autoreleasePoolPop(pool);
引用計(jì)數(shù)
獲取引用計(jì)數(shù)的函數(shù)為CFGetRetainCount
例如:
{ id __strong obj = [[NSObject alloc] init]; NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)obj)); }
結(jié)果為1
{ id __strong obj = [[NSObject alloc] init]; id __weak obj1 = obj; NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)obj)); }
結(jié)果為2
{ id __strong obj = [[NSObject alloc] init]; id __autoreleaing obj1 = obj; NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)obj)); }
結(jié)果為2
{ id __strong obj = [[NSObject alloc] init]; @autoreleasepool{ id __autoreleaing obj1 = obj; NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)obj)); } } NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)obj)); }
結(jié)果為2和1
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
IOS ObjectC與javascript交互詳解及實(shí)現(xiàn)代碼
這篇文章主要介紹了IOS OC與js交互詳解及實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03IOS 使用NSAssert()和NSParameterAssert調(diào)試程序
這篇文章主要介紹了IOS 使用NSAssert()和NSParameterAssert調(diào)試程序的相關(guān)資料,需要的朋友可以參考下2017-06-06iOS開發(fā)實(shí)現(xiàn)UIImageView的分類
這篇文章主要為大家詳細(xì)介紹了iOS開發(fā)實(shí)現(xiàn)UIImageView的分類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01iOS開發(fā)中音頻工具類的封裝以及音樂播放器的細(xì)節(jié)控制
這篇文章主要介紹了iOS開發(fā)中音頻工具類的封裝以及音樂播放器的細(xì)節(jié)控制,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-12-12iOS中FMDB事務(wù)實(shí)現(xiàn)批量更新數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了iOS中FMDB事務(wù)實(shí)現(xiàn)批量更新數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11IOS等待時(shí)動(dòng)畫效果的實(shí)現(xiàn)
查詢時(shí)間有長(zhǎng)有短,為了增強(qiáng)用戶體驗(yàn)度,目前用的比較多的手段之一是查詢等待時(shí)添加一個(gè)動(dòng)態(tài)等待效果,這篇文章主要介紹IOS等待時(shí)動(dòng)畫效果的實(shí)現(xiàn),有需要的朋友可以參考下2015-08-08iOS調(diào)試Block引用對(duì)象無法被釋放的小技巧分享
這篇文章主要給大家分享介紹了關(guān)于iOS調(diào)試Block引用對(duì)象無法被釋放的小技巧,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位iOS開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09iOS應(yīng)用中存儲(chǔ)用戶設(shè)置的plist文件的創(chuàng)建與讀寫教程
這篇文章主要介紹了iOS應(yīng)用中存儲(chǔ)用戶設(shè)置的plist文件的創(chuàng)建與讀寫教程,plist文件是在Xcode下的項(xiàng)目中會(huì)被自動(dòng)生成,里面采用XML格式記錄數(shù)據(jù),需要的朋友可以參考下2016-04-04