首先感谢@Darren,感谢您提供的链接。
我在链接中参考了一些段落。
ARC有一个技巧,如果调用者和被调用者都是ARC,则可以将返回的对象保持在autorelease池之外。
但这是如何工作的? ARC的一个特性是旧的编译器无法处理ARC代码(MRC代码)与ARC代码互相调用。但如果ARC代码不将返回的对象放入MRC代码期望的autorelease池中,则该对象将会泄漏。
因此,在返回对象时,ARC化的clang代码会发出此函数调用:objc_autoreleaseReturnValue(id)。
如果看一下objc_autoreleaseReturnValue的实现,它调用了callerAcceptsFastAutorelease()。即使您不阅读x86_64或ARM汇编语言,该代码的注释也很简单明了:
/*
Fast handling of returned autoreleased values.
The caller and callee cooperate to keep the returned object
out of the autorelease pool.
Caller:
ret = callee();
objc_retainAutoreleasedReturnValue(ret);
// use ret here
Callee:
// compute ret
[ret retain];
return objc_autoreleaseReturnValue(ret);
objc_autoreleaseReturnValue() examines the caller's instructions following
the return. If the caller's instructions immediately call
objc_autoreleaseReturnValue, then the callee omits the -autorelease and saves
the result in thread-local storage. If the caller does not look like it
cooperates, then the callee calls -autorelease as usual.
objc_autoreleaseReturnValue checks if the returned value is the same as the
one in thread-local storage. If it is, the value is used directly. If not,
the value is assumed to be truly autoreleased and is retained again. In
either case, the caller now has a retained reference to the value.
Tagged pointer objects do participate in the fast autorelease scheme,
because it saves message sends. They are not entered in the autorelease
pool in the slow case.
*/
来自我
让我们看一下objc_autoreleaseReturnValue
的源代码。
id
objc_autoreleaseReturnValue(id obj)
{
#if SUPPORT_RETURN_AUTORELEASE
assert(tls_get_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL);
if (callerAcceptsFastAutorelease(__builtin_return_address(0))) {
tls_set_direct(AUTORELEASE_POOL_RECLAIM_KEY, obj);
return obj;
}
#endif
return objc_autorelease(obj);
}
__builtin_return_address(0)
返回当前函数的返回地址,现在让我们来看一下 arm 版本的 callerAcceptsFastAutorelease
实现:
static bool callerAcceptsFastAutorelease(const void *ra)
{
if ((uintptr_t)ra & 1) {
if (*(uint16_t *)((uint8_t *)ra - 1) == 0x463f) {
return true;
}
} else {
if (*(uint32_t *)ra == 0xe1a07007) {
return true;
}
}
return false;
}
在这种方法中,它寻找指令
mov r7, r7
,这是
objc_retainAutoreleaseReturnValue
的标记。如果找到了,那么该方法将返回
true
,因此被调用者将省略自动释放。
您可以查看调用者的汇编代码,即我的情况下的
testFun1
。
- (void)testFun1:(ViewController *)vc
{
[vc createATestObj] ;
}
以下是汇编代码,你可以找到这行代码 "mov r7, r7 @ marker for objc_retainAutoreleaseReturnValue"
.cfi_startproc
@ BB#0:
push {r7, lr}
mov r7, sp
sub sp, #16
add r3, sp, #4
movw r9, #0
movt r9, #0
str r0, [sp, #12]
str r1, [sp, #8]
str.w r9, [sp, #4]
mov r0, r3
mov r1, r2
bl _objc_storeStrong
movw r0, :lower16:(L_objc_msgSend$non_lazy_ptr-(LPC5_0+4))
movt r0, :upper16:(L_objc_msgSend$non_lazy_ptr-(LPC5_0+4))
LPC5_0:
add r0, pc
ldr r0, [r0]
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_12-(LPC5_1+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_12-(LPC5_1+4))
LPC5_1:
add r1, pc
Ltmp14:
ldr r2, [sp, #4]
ldr r1, [r1]
str r0, [sp] @ 4-byte Spill
mov r0, r2
ldr r2, [sp] @ 4-byte Reload
blx r2
@ InlineAsm Start
mov r7, r7 @ marker for objc_retainAutoreleaseReturnValue
.code 16
@ InlineAsm End
bl _objc_retainAutoreleasedReturnValue
bl _objc_release
movs r1, #0
movt r1, #0
add r0, sp, #4
bl _objc_storeStrong
add sp, #16
pop {r7, pc}
Ltmp15:
Lfunc_end5:
.cfi_endproc
更新:
我认为在苹果的实现中有一些评论是错误的。
正确的评论应该是这样的:
/*
Fast handling of returned autoreleased values.
The caller and callee cooperate to keep the returned object
out of the autorelease pool.
Caller:
ret = callee();
objc_retainAutoreleasedReturnValue(ret);
// use ret here
Callee:
// compute ret
[ret retain];
return objc_autoreleaseReturnValue(ret);
objc_autoreleaseReturnValue() examines the caller's instructions following
the return. If the caller's instructions immediately call
objc_retainAutoreleasedReturnValue, then the callee omits the -autorelease and saves
the result in thread-local storage. If the caller does not look like it
cooperates, then the callee calls -autorelease as usual.
objc_retainAutoreleasedReturnValue checks if the returned value is the same as the
one in thread-local storage. If it is, the value is used directly. If not,
the value is assumed to be truly autoreleased and is retained again. In
either case, the caller now has a retained reference to the value.
Tagged pointer objects do participate in the fast autorelease scheme,
because it saves message sends. They are not entered in the autorelease
pool in the slow case.
*/
如果您有其他的想法,请留下评论。 谢谢!