本文使用的 runtime 版本為 objc4-706。
創(chuàng)建對(duì)象
所謂創(chuàng)建對(duì)象,也就是方法名為 alloc、new、copy 或者 mutableCopy 開頭的情況,比如下面的代碼:
NSObject * __strong obj = [[NSObject alloc] init];
這時(shí)不會(huì)有任何多余的工作進(jìn)行。
在《Objective-C 小記(6)alloc & init》中有記錄,創(chuàng)建完對(duì)象后對(duì)象的引用計(jì)數(shù)已經(jīng)是 1。
賦值
在給 __strong 變量賦值時(shí),會(huì)調(diào)用一個(gè) runtime 函數(shù):
obj = otherObj;
// 會(huì)變成如下函數(shù)調(diào)用
objc_storeStrong(&obj, otherObj);
objc_storeStrong 的實(shí)現(xiàn)如下:
void
objc_storeStrong(id *location, id obj)
{
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj);
*location = obj;
objc_release(prev);
}
可以看到,函數(shù)會(huì)對(duì)新的對(duì)象進(jìn)行 retain 操作,對(duì)老的對(duì)象進(jìn)行 release 操作,和 MRC 下是一致的。
關(guān)于
retain和release的實(shí)現(xiàn)可以閱讀《Objective-C 小記(7)retain & release》。
變量的銷毀
在 __strong 變量離開其范圍時(shí),會(huì)進(jìn)行置 nil 的操作:
{
NSObject * __strong obj = [[NSObject alloc] init];
// 下面的代碼會(huì)被調(diào)用,等同于 obj = nil;
objc_storeStrong(&obj, nil);
}
實(shí)際上這樣就等同于銷毀前對(duì)指向的對(duì)象發(fā)送 release 消息,也是和 MRC 下一致的行為。
返回對(duì)象的方法
非 alloc、new、copy 或 mutableCopy 開頭的方法返回對(duì)象,會(huì)是什么情況呢?比如如下的操作:
KRObject *object = [KRObject object];
如果和 MRC 下的行為一致的話,這里只要直接進(jìn)行 retain 操作其實(shí)就可以了,但 ARC 對(duì)這種情況進(jìn)行了優(yōu)化嘗試。
先看到 +object 方法的實(shí)現(xiàn),它在返回的時(shí)候會(huì)調(diào)用 objc_autoreleaseReturnValue 函數(shù):
+ (instancetype)object {
return [[KRObject alloc] init];
// 會(huì)變成這樣
return objc_autoreleaseReturnValue([[KRObject alloc] init]);
}
objc_autoreleaseReturnValue 的實(shí)現(xiàn):
// Prepare a value at +1 for return through a +0 autoreleasing convention.
id
objc_autoreleaseReturnValue(id obj)
{
if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;
return objc_autorelease(obj);
}
函數(shù)是嘗試返回一個(gè)沒有 autorelease 過的對(duì)象,這樣如 +object 類似的方法的調(diào)用者就不需要在 retain 一遍了,同時(shí)也能減少 autorelease pool 里的對(duì)象。很明顯,prepareOptimizedReturn 函數(shù)就是用來確定能不能使用這個(gè)優(yōu)化的,可以的話直接返回對(duì)象即可,否則正常的進(jìn)行 autorelease。
enum ReturnDisposition : bool {
ReturnAtPlus0 = false, ReturnAtPlus1 = true
};
// Try to prepare for optimized return with the given disposition (+0 or +1).
// Returns true if the optimized path is successful.
// Otherwise the return value must be retained and/or autoreleased as usual.
static ALWAYS_INLINE bool
prepareOptimizedReturn(ReturnDisposition disposition)
{
assert(getReturnDisposition() == ReturnAtPlus0);
if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {
if (disposition) setReturnDisposition(disposition);
return true;
}
return false;
}
callerAcceptsOptimizedReturn 函數(shù)根據(jù) CPU 架構(gòu)不同實(shí)現(xiàn)也不相同(而且我看不懂……),總之是根據(jù)返回地址來確定調(diào)用者是否支持這個(gè)優(yōu)化操作的。getReturnDisposition 和 setReturnDisposition 則是使用了 TLS,和線程綁定的:
static ALWAYS_INLINE ReturnDisposition
getReturnDisposition()
{
return (ReturnDisposition)(uintptr_t)tls_get_direct(RETURN_DISPOSITION_KEY);
}
static ALWAYS_INLINE void
setReturnDisposition(ReturnDisposition disposition)
{
tls_set_direct(RETURN_DISPOSITION_KEY, (void*)(uintptr_t)disposition);
}
回到 objc_autoreleaseReturnValue 函數(shù)來說,要是支持優(yōu)化的返回的話,會(huì)將線程存儲(chǔ)的標(biāo)志設(shè)置成 ReturnAtPlus1 也就是 true。
之后繼續(xù)分析調(diào)用者:
KRObject *object = [KRObject object];
// 會(huì)變成
KRObject *object = objc_retainAutoreleasedReturnValue([KRObject object]);
也就是會(huì)調(diào)用 objc_retainAutoreleasedReturnValue 函數(shù):
// Accept a value returned through a +0 autoreleasing convention for use at +1.
id
objc_retainAutoreleasedReturnValue(id obj)
{
if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;
return objc_retain(obj);
}
// Try to accept an optimized return.
// Returns the disposition of the returned object (+0 or +1).
// An un-optimized return is +0.
static ALWAYS_INLINE ReturnDisposition
acceptOptimizedReturn()
{
ReturnDisposition disposition = getReturnDisposition();
setReturnDisposition(ReturnAtPlus0); // reset to the unoptimized state
return disposition;
}
可以看到,當(dāng) objc_retainAutoreleasedReturnValue 發(fā)現(xiàn)線程中的標(biāo)志被設(shè)置成 ReturnAtPlus1 時(shí),直接返回對(duì)象(因?yàn)榉祷刂皼]有進(jìn)行 autorelease),否則進(jìn)行 retain 操作(方法返回時(shí)進(jìn)行了 autorelease 操作)。
總結(jié)
不加上最后方法返回的這一個(gè)優(yōu)化的話,__strong 其實(shí)只要編譯器就足夠了,但因?yàn)榧由狭艘粋€(gè)優(yōu)化,現(xiàn)在是一個(gè)還需要 runtime 支持的事情了。