objc_autoreleaseReturnValue是什么意思?

4

我有一个方法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 value);

前提条件:value为空或指向有效对象的指针。

如果value为null,则此调用无效果。否则,它会尽力将对对象的保留计数的所有权移交给封闭调用帧中的objc_retainAutoreleasedReturnValue对同一对象的调用。如果这不可能,则像上面那样自动释放该对象。

始终返回value。


2
这是一种优化,当调用者和被调用者都具有ARC意识时,避免将返回的对象添加到自动释放池中。更多信息请参见:http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease - Darren
1个回答

10

首先感谢@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 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 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.
*/

如果您有其他的想法,请留下评论。 谢谢!


FYI,我认为现在不再是callerAcceptsFastAutorelease了。我认为它是由prepareOptimizedReturn调用的callerAcceptsOptimizedReturn,位于https://github.com/opensource-apple/objc4/blob/master/runtime/objc-object.h中。 - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接