Objective-C 小記(9)__strong

本文使用的 runtime 版本為 objc4-706。

創(chuàng)建對(duì)象

所謂創(chuàng)建對(duì)象,也就是方法名為 alloc、newcopy 或者 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)于 retainrelease 的實(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ì)象的方法

allocnew、copymutableCopy 開頭的方法返回對(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)化操作的。getReturnDispositionsetReturnDisposition 則是使用了 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 支持的事情了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容