Что означает objc_autoreleaseReturnValue?
У меня есть метод createATestObject
, Как видно из его названия, он создает объект и возвращает его. Код очень прост и находится под 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
и получить ссылку об этом здесь. Но я не знал, что это значит. Кто-нибудь может это уточнить? Спасибо большое.
id objc_autoreleaseReturnValue (значение id);
Условие: значение равно нулю или указатель на действительный объект.
Если значение равно нулю, этот вызов не имеет никакого эффекта. В противном случае он делает все возможное, чтобы передать право владения счетчиком сохранения для объекта вызову objc_retainAutoreleasedReturnValue для того же объекта во вмещающем кадре вызова. Если это невозможно, объект автоматически освобождается, как указано выше.
Всегда возвращает значение.
1 ответ
Сначала спасибо @Darren, спасибо за вашу ссылку.
Я ссылаюсь на какой-то параграф в ссылке.
У ARC есть хитрость, которая удерживает возвращаемые объекты от пулов авто-релиза, если и вызывающий, и вызываемый являются ARC.
Но как это работает? Одной из особенностей ARC является то, что старый код, скомпилированный перед ARC (код MRC), может вызывать код ARC и наоборот. Но если код ARC не помещает возвращаемый объект в пул автоматического выпуска, который ожидает код MRC, тогда объект просто утечет.
Таким образом, код Clang с поддержкой ARC генерирует этот вызов функции при возврате объекта: 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)
возвращает адрес возврата текущей функции, тогда давайте посмотрим на callerAcceptsFastAutorelease
Реализация для ручной версии:
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
, если он найдет это, то метод возврата true
, так что вызываемый будет опускать авто-релиз.
Вы можете увидеть код сборки звонящего, который testFun1
в моем случае.
- (void)testFun1:(ViewController *)vc
{
[vc createATestObj] ;
}
Ниже приведен код сборки, вы можете найти строку "mov r7, r7 @ marker для 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.
Правильный комментарий должен быть таким:
/*
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.
*/
Оставьте мне комментарий, если вы не согласны со мной. Спасибо!