本文使用的 runtime 版本为 objc4-706

创建对象

所谓创建对象,也就是方法名为 allocnewcopy 或者 mutableCopy 开头的情况,比如下面的代码:

NSObject * __strong obj = [[NSObject alloc] init];

这时不会有任何多余的工作进行。

Objective-C 小记(6)alloc & init 中有记录,创建完对象后对象的引用计数已经是 1。

赋值

在给 __strong 变量赋值时,会调用一个 runtime 函数:

obj = otherObj;

// 会变成如下函数调用
objc_storeStrong(&obj, otherObj);

objc_storeStrong 的实现如下:

void
objc_storeStrong(id *location, id obj)
{
    id prev = *location;
    if (obj == prev) {
        return;
    }
    objc_retain(obj);
    *location = obj;
    objc_release(prev);
}

可以看到,函数会对新的对象进行 retain 操作,对老的对象进行 release 操作,和 MRC 下是一致的。

关于 retainrelease 的实现可以阅读 Objective-C 小记(7)retain & release

变量的销毁

__strong 变量离开其范围时,会进行置 nil 的操作:

{
    NSObject * __strong obj = [[NSObject alloc] init];
    
    // 下面的代码会被调用,等同于 obj = nil;
    objc_storeStrong(&obj, nil);
}

实际上这样就等同于销毁前对指向的对象发送 release 消息,也是和 MRC 下一致的行为。

返回对象的方法

allocnewcopymutableCopy 开头的方法返回对象,会是什么情况呢?比如如下的操作:

KRObject *object = [KRObject object];

如果和 MRC 下的行为一致的话,这里只要直接进行 retain 操作其实就可以了,但 ARC 对这种情况进行了优化尝试。

先看到 +object 方法的实现,它在返回的时候会调用 objc_autoreleaseReturnValue 函数:

+ (instancetype)object {
    return [[KRObject alloc] init];
    
    // 会变成这样
    return objc_autoreleaseReturnValue([[KRObject alloc] init]);
}

objc_autoreleaseReturnValue 的实现:

// 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);
}

函数是尝试返回一个没有 autorelease 过的对象,这样如 +object 类似的方法的调用者就不需要在 retain 一遍了,同时也能减少 autorelease pool 里的对象。很明显,prepareOptimizedReturn 函数就是用来确定能不能使用这个优化的,可以的话直接返回对象即可,否则正常的进行 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 函数根据 CPU 架构不同实现也不相同(而且我看不懂……),总之是根据返回地址来确定调用者是否支持这个优化操作的。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 函数来说,要是支持优化的返回的话,会将线程存储的标志设置成 ReturnAtPlus1 也就是 true

之后继续分析调用者:

KRObject *object = [KRObject object];

// 会变成
KRObject *object = objc_retainAutoreleasedReturnValue([KRObject object]);

也就是会调用 objc_retainAutoreleasedReturnValue 函数:

// 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;
}

可以看到,当 objc_retainAutoreleasedReturnValue 发现线程中的标志被设置成 ReturnAtPlus1 时,直接返回对象(因为返回之前没有进行 autorelease),否则进行 retain 操作(方法返回时进行了 autorelease 操作)。

总结

不加上最后方法返回的这一个优化的话,__strong 其实只要编译器就足够了,但因为加上了一个优化,现在是一个还需要 runtime 支持的事情了。