您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Objective-C中如何實現ARC,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
前言
在ARC出現以前,程序員們只能靠retain/relese/autorelease來確保對象們恰好“堅持”到被需要的那一刻。如果忘了retain,或者多次release某個對象,程序就會發生內存泄漏的問題,甚至直接崩潰。
在Xcode 4.2中,除了語法檢查外,Apple的新LLVM編譯器還將內存管理的苦差事接了過來,它會檢查代碼,決定何時釋放對象。Apple的文檔里是這么定義ARC的:
“自動引用計數(ARC)是一個編譯器級的功能,它能簡化Cocoa應用中對象生命周期管理(內存管理)的流程。”
ARC使內存管理在大部分時候變得如同小事一樁,但我們仍要在決定自己的類如何管理其它對象的引用時承擔一些責任。
蘋果的官方說明中稱,ARC是“由編譯器進行內存管理”的,但是實際上只有編譯其是無法完全勝任的,再次基礎上還需要Objective-C運行時庫的協助。
也就是說,ARC由以下工具、庫來實現。
clang(LLVM編輯器)
objc4 Objective-C 運行時庫
__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);
如上所示,調用了2次objc_msgSend方法,變量的作用域結束時通過objc_release釋放對象。雖然ARC有效時不能使用release方法,但由此可知編譯器自動插入了release。
使用alloc/new/copy/mutableCopy以外的方法時:
{ id __strong obj = [NSMutableArray array]; }
編譯器的模擬代碼如下:
/** 編譯器的模擬代碼 */ id obj = objc_msgSend(NSMutableArray,@selector(array)); objc_retainAutoreleasedReturnValue(obj); objc_release(obj);
其中的objc_retainAutoreleasedReturnValue函數主要用于優化程序運行,它用于自己持有(retain)對象的函數,但它持有的對象應為返回注冊在autoreleasepool中對象的方法,或是函數的返回值。在調用alloc/new/copy/mutableCopy以外的方法,由編譯器插入該函數。
與 objc_retainAutoreleaseReturnValue函數相對的函數為objc_autoreleaseReturnValue函數。它用于alloc/new/copy/mutableCopy方法以外的NSMutableArray類的array類方法等返回對象的實現上。
+ (id) array { return [[NSArray alloc] init]; }
轉換后
+ (id) array { id obj = objc_msgSend(NSArray,@selector(alloc)); objc_msgSend(obj,@selector(init)); return objc_autoreleaseReturnValue(obj); }
返回注冊到autoreleasepool中對象的方法使用了objc_autoreleaseReturValue函數返回注冊到autoreleasepool中的對象。但是objc_autoreleaseReturValue函數同objc_autorelease函數不同,一般不僅限于注冊對象到autoreleasepool中。
objc_autoreleaseReturValue函數會檢查使用該函數的方法或函數調用方的執行命令列表。如果方法或函數的調用方在調用了方法或函數后緊接著調用objc_retainAutoreleasedReturnValue()函數,那么就不將返回的對象注冊到autoreleasepool中,而直接傳遞到方法或函數的調用方。objc_retainAutoreleasedReturnValue函數與objc_retain函數不同,它即便不注冊到autoreleasepool中而返回對象,也能夠正確地獲取對象。
通過objc_autoreleaseReturnValue函數和objc_retainAutoreleasedReturnValue函數的協作,可以不講對象注冊到autoreleasepool中而直接傳遞,這一過程達到了最優化。
__weak 修飾符
若附有__weak修飾符的變量所引用的對象被廢棄,則將nil賦值給該變量。
使用附有__weak修飾符的變量,即是使用注冊到了autoreleasepool中的對象。
{ id __weak obj1 = obj; }
/** 編譯器的模擬代碼 */ id obj1; objc_initWeak(&obj1,obj); objc_destroyWeak(&obj1);
通過objc_initWeak函數初始化附有__weak修飾符的變量,在變量作用域結束時通過objc_destroyWeak函數釋放該變量。
如以下源碼所示,objc_initWeak函數將附有__weak修飾符的變量初始化為0后,會將賦值的對象作為參數調用objc_storeWeak函數。
obj1 = 0; objc_storeWeak(&obj1,obj);
objc_destroyWeak函數將0作為參數調用objc_storeWeak函數。
objc_storeWeak(&obj1,0);
即前面的源代碼和以下代碼相同:
id obj1; obj1 = 0; objc_storeWeak(&obj1,obj); objc_storeWeak(&obj1,0);
objc_weakStore函數把第二參數的復制對象的地址作為鍵值,將第一參數的附有__weak修飾符的變量的地址注冊到weak表中,如果第二參數為0,則把變量的地址從weak表中刪除。
weak表與引用計數表相同,作為散列表被實現。如果使用weak表,將廢棄對象的地址作為鍵值進行檢索,能高速地獲取對應的附有__weak修飾符的變量的地址。另外,由于一個對象可以同時賦值給多個附有__weak修飾符的變量中,所以對于一個鍵值,可注冊多個變量的地址。
釋放對象時,廢棄誰都不持有的對象,通過objc_release函數釋放。
objc_release
因為計數為0,所以執行dealloc
_objc_rootDealloc
object_dispose
objc_destructInstanse
objc_clear_deallocating
對象被廢棄時最后調用的objc_clear_deallocating函數動作如下:
從weak表中獲取廢棄對象的地址為鍵值的記錄。
將包含在記錄中的所有附有__weak修飾符變量的地址,賦值為nil
從weak表中刪除該記錄。
從引用計數表中刪除廢棄對象的地址作為鍵值的記錄。
根據以上步驟,前面說的如果附有__weak修飾符的變量所引用的對象被廢棄,則將nil賦值給該變量這一功能即被實現。由此可知,如果大量使用附有__weak修飾符的變量,則會消耗相應的CPU資源,對此只在需要避免循環引用的時候使用__weak修飾符。
使用__weak修飾符時,以下代碼會引起編譯器警告
{ id __weak obj = [[NSObject alloc] init]; NSLog(@"obj = %@",obj); }
編譯結果如下:
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);
運行結果如下:
2017-12-07 19:37:24.075939+0800 ImageOrientation[10963:3581164] obj = (null)
使用附有__weak修飾符的變量,即是使用注冊到autoreleasepool中的對象。
{ id __weak obj1 = obj; NSLog(@"%@",obj1); }
該代碼可以轉換為如下形式:
/** 編譯器模擬代碼*/ id obj1; objc_initWeak(&obj1,obj); id temp = objc_loadWeakRetained(&obj1); objc_autorelease(temp); NSLog(@"%@",obj1); objc_destroyWeak(&obj1);
與賦值時相比,在使用附有__weak修飾符變量的情形下,增加了對objc_loadWeakRetained函數和objc_autorelease函數的調用。這些函數的動作如下:
objc_loadWeakRetained函數取出附有__weak修飾符變量所引用的對象并retain
objc_autorelease函數將對象注冊到autoreleasepool中。
__autoreleasing 修飾符
將對象賦值給附有__autoreleasing修飾符的變量等同于MRC時調用對象的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之外的方法實現:
@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);
引用計數
獲取引用計數的函數為CFGetRetainCount
例如:
{ id __strong obj = [[NSObject alloc] init]; NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)obj)); }
結果為1
{ id __strong obj = [[NSObject alloc] init]; id __weak obj1 = obj; NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)obj)); }
結果為2
{ id __strong obj = [[NSObject alloc] init]; id __autoreleaing obj1 = obj; NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)obj)); }
結果為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)); }
結果為2和1
關于“Objective-C中如何實現ARC”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。