本文使用的 runtime 版本为 objc4-706。
创建对象
所谓创建对象,也就是方法名为 alloc
、new
、copy
或者 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 下是一致的。
关于
retain
和release
的实现可以阅读《Objective-C 小记(7)retain & release》。
变量的销毁
在 __strong
变量离开其范围时,会进行置 nil
的操作:
{
NSObject * __strong obj = [[NSObject alloc] init];
// 下面的代码会被调用,等同于 obj = nil;
objc_storeStrong(&obj, nil);
}
实际上这样就等同于销毁前对指向的对象发送 release
消息,也是和 MRC 下一致的行为。
返回对象的方法
非 alloc
、new
、copy
或 mutableCopy
开头的方法返回对象,会是什么情况呢?比如如下的操作:
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 架构不同实现也不相同(而且我看不懂……),总之是根据返回地址来确定调用者是否支持这个优化操作的。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
函数来说,要是支持优化的返回的话,会将线程存储的标志设置成 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 支持的事情了。