Objective c 为什么ARC保留方法参数?

Objective c 为什么ARC保留方法参数?,objective-c,automatic-ref-counting,clang,Objective C,Automatic Ref Counting,Clang,使用ARC编译时,方法参数通常在方法开始时保留,在方法结束时释放。这个保留/释放对似乎是多余的,并且与ARC“生成您无论如何都会编写的代码”的想法相矛盾。在那些黑暗、弧前的日子里,为了安全起见,没有人对所有方法参数执行额外的保留/释放,是吗 考虑: @interface Test : NSObject @end @implementation Test - (void)testARC:(NSString *)s { [s length]; // no extra retain/rele

使用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_retain
objc_release
的调用。发生什么事了

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

请参见Objc语言邮件列表:


当编译器不知道关于 函数或方法的内存管理行为(这种情况发生在 则编译器必须假定:

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

给定#1和#2;考虑到弧线决不能允许物体 如果过早地取消分配,那么这两个假设将迫使编译器 更频繁地保留传入的对象

我认为主要的问题是您的方法主体可能导致参数被释放,因此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
}

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

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

作为参数传递通常不会增加保留计数。但是,如果要将其传递给类似
NSThread
的对象,则特别说明它将保留新线程的参数


因此,如果没有一个你打算如何开始这个新的线程的例子,我不能给出一个明确的答案。不过,总的来说,你应该没事

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

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

你能把它优化掉吗?这就像优化局部变量上的每个保留/释放对,因为参数是局部变量。如果编译器理解方法中的孔代码,包括发送的所有消息和函数调用,则可以完成此操作。这可以应用到很少有叮当声甚至不尝试这样做。(假设arg指向(仅)属于某个组的人,并且该组已解除锁定:该人也将解除锁定。)


是的,在MRC中不保留arg是一种危险,但通常开发人员都知道他们的代码很好,他们在没有考虑的情况下优化了保留/发布过程。

知道为什么在第一个示例中不会发生这种情况吗?您始终可以使用反汇编助手视图查看实际生成的代码。简而言之,在第二个示例中,对
[s length]
的第一次调用可能会导致
s
被解除分配,除非它首先被保留,因此第二次调用可能是在一个不存在的对象上。“编译器对函数或方法的内存管理行为一无所知”。有人会认为,它可以(或被苹果公司告知)指出NSString:length表现良好。当编译器有足够的有用信息时,是否有示例?还是所有方法调用都会发生这种情况?我想这太难了,不值得。“表现良好”可能会归结为“除了局部变量之外,不会释放任何东西”,这是一个相当有力的保证。对于大多数人来说,方法参数的额外保留和释放的成本实际上是零,当它真的伤害你时,
\uu unsafe\u unrepaired
解决了这个问题,如果你知道你在做什么;ARC假设您的代码不执行任何“未激活”的释放/保留对(不要与保留/释放对混淆)。正确的理由与线程安全有关,因为我们希望防止其他线程在我们仍在使用参数时解除锁定我们的参数。诚然,我很难想出一个例子,在实践中这可能是一个问题,因为如果另一个线程运行得足够快,它仍然可以在我们到达添加的
retain
之前释放我们的参数。但是“保留你所使用的”仍然是一个很好的一般原则!