objc_autoreleaseReturnValue 是什么意思?
What does objc_autoreleaseReturnValue mean?
我有一个方法createATestObject
。顾名思义,它创建一个对象并 return 它。代码很简单,在ARC下
- (TestObj *)createATestObj
{
return [[TestObj alloc] init] ;
}
我汇编文件,得到下面的汇编代码。
Lfunc_begin4:
.cfi_startproc
@ BB#0:
push {r7, lr}
mov r7, sp
sub sp, #8
@DEBUG_VALUE: -[ViewController createATestObj]:self <- undef
@DEBUG_VALUE: -[ViewController createATestObj]:_cmd <- undef
str r0, [sp, #4]
str r1, [sp]
movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC4_0+4))
movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC4_0+4))
LPC4_0:
add r0, pc
ldr r0, [r0]
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_10-(LPC4_1+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_10-(LPC4_1+4))
LPC4_1:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC4_2+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC4_2+4))
LPC4_2:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
add sp, #8
pop.w {r7, lr}
b.w _objc_autoreleaseReturnValue
Ltmp13:
Lfunc_end4:
.cfi_endproc
我注意到函数 _objc_autoreleaseReturnValue
并从 here 获得了关于它的参考。但我不知道这是什么意思。任何人都可以详细说明吗?非常感谢。
id objc_autoreleaseReturnValue(id value);
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it makes a best
effort to hand off ownership of a retain count on the object to a call
to objc_retainAutoreleasedReturnValue for the same object in an
enclosing call frame. If this is not possible, the object is
autoreleased as above.
Always returns value.
首先感谢@Darren,感谢您的link。
我引用了link中的一些段落。
ARC has a trick that keeps returned objects out of autorelease pools
if both the caller and callee are ARC.
But how does that work? One of
the features of ARC is that old compiled-before-ARC code (MRC code)
can call ARC code and vice-versa. But if ARC code doesn’t put a
returned object in an autorelease pool that MRC code is expecting,
then the object would just leak.
So ARC-ified clang code emits this
function call when returning an object:
objc_autoreleaseReturnValue(id).
If you look at
objc_autoreleaseReturnValue's implementation, it calls
callerAcceptsFastAutorelease(). Even if you don’t read x86_64 or ARM
assembly, the code’s comment is straight-forward:
/*
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)
returns the return address of current function,然后我们看一下callerAcceptsFastAutorelease
对于arm版本的实现:
static bool callerAcceptsFastAutorelease(const void *ra)
{
// if the low bit is set, we're returning to thumb mode
if ((uintptr_t)ra & 1) {
// 3f 46 mov r7, r7
// we mask off the low bit via subtraction
if (*(uint16_t *)((uint8_t *)ra - 1) == 0x463f) {
return true;
}
} else {
// 07 70 a0 e1 mov r7, r7
if (*(uint32_t *)ra == 0xe1a07007) {
return true;
}
}
return false;
}
在方法中,它查找指令mov r7, r7
,它是objc_retainAutoreleaseReturnValue
的标记,如果找到,则方法return 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
更新:
我认为apple's implementation的评论有一些错误。
正确的评论应该是这样的:
/*
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.
*/
如果您有其他想法,请发表评论。谢谢!
我有一个方法createATestObject
。顾名思义,它创建一个对象并 return 它。代码很简单,在ARC下
- (TestObj *)createATestObj
{
return [[TestObj alloc] init] ;
}
我汇编文件,得到下面的汇编代码。
Lfunc_begin4:
.cfi_startproc
@ BB#0:
push {r7, lr}
mov r7, sp
sub sp, #8
@DEBUG_VALUE: -[ViewController createATestObj]:self <- undef
@DEBUG_VALUE: -[ViewController createATestObj]:_cmd <- undef
str r0, [sp, #4]
str r1, [sp]
movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC4_0+4))
movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC4_0+4))
LPC4_0:
add r0, pc
ldr r0, [r0]
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_10-(LPC4_1+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_10-(LPC4_1+4))
LPC4_1:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC4_2+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC4_2+4))
LPC4_2:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
add sp, #8
pop.w {r7, lr}
b.w _objc_autoreleaseReturnValue
Ltmp13:
Lfunc_end4:
.cfi_endproc
我注意到函数 _objc_autoreleaseReturnValue
并从 here 获得了关于它的参考。但我不知道这是什么意思。任何人都可以详细说明吗?非常感谢。
id objc_autoreleaseReturnValue(id value);
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it makes a best effort to hand off ownership of a retain count on the object to a call to objc_retainAutoreleasedReturnValue for the same object in an enclosing call frame. If this is not possible, the object is autoreleased as above.
Always returns value.
首先感谢@Darren,感谢您的link。
我引用了link中的一些段落。
ARC has a trick that keeps returned objects out of autorelease pools if both the caller and callee are ARC.
But how does that work? One of the features of ARC is that old compiled-before-ARC code (MRC code) can call ARC code and vice-versa. But if ARC code doesn’t put a returned object in an autorelease pool that MRC code is expecting, then the object would just leak.
So ARC-ified clang code emits this function call when returning an object: objc_autoreleaseReturnValue(id).
If you look at objc_autoreleaseReturnValue's implementation, it calls callerAcceptsFastAutorelease(). Even if you don’t read x86_64 or ARM assembly, the code’s comment is straight-forward:
/*
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)
returns the return address of current function,然后我们看一下callerAcceptsFastAutorelease
对于arm版本的实现:
static bool callerAcceptsFastAutorelease(const void *ra)
{
// if the low bit is set, we're returning to thumb mode
if ((uintptr_t)ra & 1) {
// 3f 46 mov r7, r7
// we mask off the low bit via subtraction
if (*(uint16_t *)((uint8_t *)ra - 1) == 0x463f) {
return true;
}
} else {
// 07 70 a0 e1 mov r7, r7
if (*(uint32_t *)ra == 0xe1a07007) {
return true;
}
}
return false;
}
在方法中,它查找指令mov r7, r7
,它是objc_retainAutoreleaseReturnValue
的标记,如果找到,则方法return 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
更新:
我认为apple's implementation的评论有一些错误。
正确的评论应该是这样的:
/*
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.
*/
如果您有其他想法,请发表评论。谢谢!