20

使用 ARC 编译时,方法参数通常会出现在方法的开头保留并在结尾释放。这个保留/释放对似乎是多余的,并且与 ARC“生成您无论如何都会编写的代码”的想法相矛盾。在那些黑暗的、ARC 之前的日子里,没有人为了安全起见对所有方法参数执行额外的保留/释放,是吗?

考虑:

@interface Test : NSObject
@end

@implementation Test

- (void)testARC:(NSString *)s
{
  [s length];  // no extra retain/release here.
}

- (void)testARC2:(NSString *)s
{
  // ARC inserts [s retain]
  [s length];
  [s length];
  // ARC inserts [s release]
}

- (void)testARC3:(__unsafe_unretained NSString *)s
{
  // no retain -- we used __unsafe_unretained
  [s length];
  [s length];
  // no release -- we used __unsafe_unretained
}

@end

在发布模式下使用 Xcode 4.3.2 编译时,程序集(以便我能够理解它)包含对第二种方法的开始objc_retainobjc_release结束处的调用。这是怎么回事?

这不是一个大问题,但是在使用 Instruments 分析性能敏感代码时,确实会出现这种额外的保留/释放流量。__unsafe_unretained正如我在第三个示例中所做的那样,您似乎可以使用 来装饰方法参数以避免这种额外的保留/释放,但这样做感觉很恶心。

4

4 回答 4

21

请参阅Objc 语言邮件列表中的回复:

当编译器对函数或方法的内存管理行为一无所知(这种情况经常发生)时,编译器必须假设:

1) 函数或方法可能会完全重新排列或替换应用程序的整个对象图(可能不会,但可以)。2)调用者可能是手动引用计数代码,因此传入参数的生命周期实际上是不可知的。

给定#1和#2;并且鉴于 ARC不能允许对象被过早地释放,那么这两个假设会迫使编译器更频繁地保留传入的对象。

我认为主要问题是您的方法的主体可能会导致参数被释放,因此 ARC 必须采取防御措施并保留它们:

- (void) processItems
{
    [self setItems:[NSArray arrayWithObject:[NSNumber numberWithInt:0]]];
    [self doSomethingSillyWith:[items lastObject]];
}

- (void) doSomethingSillyWith: (id) foo
{
    [self setItems:nil];
    NSLog(@"%@", foo); // if ARC did not retain foo, you could be in trouble
}

这也可能是当您的方法中只有一个调用时您看不到额外保留的原因。

于 2012-04-25T07:32:33.113 回答
2

通常,作为参数传递不会增加保留计数。但是,如果您将它传递给类似的东西NSThread,则专门记录了它将保留新线程的参数。

因此,如果没有您打算如何开始这个新线程的示例,我无法给出明确的答案。不过,总的来说,您应该没问题。

于 2012-07-17T12:44:38.860 回答
2

即使灵魂的答案是正确的,它也比它应该的更深了一点:

它被保留,因为传递的引用被分配给一个强变量,即参数变量。这也是唯一的原因是保留/释放对。(将参数 var 设置为 __weak 会发生什么?)

可以优化它吗?这就像优化局部变量上的每个保留/释放对一样,因为参数是局部变量。如果编译器理解方法内的漏洞代码,包括所有发送的消息和函数调用,则可以做到这一点。这可以应用到很少的那个clang甚至不尝试这样做。(想象一下,arg 指向一个(仅)属于一个组的人,并且该组被解除分配:该人也将被解除分配。)

是的,不在 MRC 中保留 args 是一种危险,但通常开发人员知道他们的代码很好,他们优化了保留/释放而不考虑它。

于 2014-05-22T15:41:06.020 回答
1

它不会在幕后增加。在 ARC 下,如果对象是 Strong ,它将简单地保持活动状态,直到没有更多指向它的强指针。但这实际上与是否作为参数传递的对象无关。

于 2012-07-17T12:29:16.697 回答