在ARC中,为什么init方法中的self是消耗参数?

时间:2013-06-21 18:48:32

标签: objective-c automatic-ref-counting

我想了解ARC,我正在读这个:

  

http://clang.llvm.org/docs/AutomaticReferenceCounting.html#consumed-parameters

它表示消耗的参数在调用之前保留,并在函数结束时释放(在函数体内)。然后它说init...方法有效标记为ns_consumes_self。我没有看到这一点。

Foo *foo = [[Foo alloc] initWithWhatever: x];

所以alloc返回一个保留计数为1的对象,对吧?现在它再次保留 ,然后进入init,然后在init结束时释放,所以我们回到1.为什么它是这样设计的?当我想到典型的init看起来像什么时,我会更加困惑。

self = [super init];
if (self) { ... } 
return self;

2 个答案:

答案 0 :(得分:8)

  

所以alloc返回一个保留计数为1的对象,对吧?现在它是   在进入init之前再次保留,然后在结束时释放   init,所以我们回到1.为什么这样设计?

可能是因为初始化器可以自由地返回指向与self最初指向的对象不同的对象的指针。一个-init方法可以说:我不喜欢这个对象,我想我会替换另一个,这完全没问题。在ARC之前,这样的初始化程序将显式释放self(即使它从未保留它),然后为其指定一些其他指针。据推测,ns_consumes_self指令将处理释放传递给初始值设定项的对象,即使self被更改为指向方法内的其他对象。

  

当我想到典型的init看起来像什么时,我会更加困惑。

这是一个很好的选择,该行为可以涵盖看起来不像典型的 -init方法的情况。修改self并不是典型的,只是允许。

答案 1 :(得分:4)

为什么不应该那样?对-init的调用意味着返回具有+1保留计数的给定对象,并且执行“临时保留”是保证自身在整个给定init方法中保持活动的最安全方式。考虑如果我们剥离Objective-C抽象层并将-init转换为IMP解析为的函数指针会发生什么:

id init(id self, SEL _cmd) {
    //+0 self given requires explicit retain to guarantee lifetime
    //...
    //(if retained) +1 self is returned; (if not retained) +0 self is deallocated
}

如果给self + +保留计数(在大多数保留其参数的方法中都很常见,即设置者),那么你必须保证在继承链的某个地方有人足够好保留自我远离任何发生的事情(以self结尾,保留计数相当模糊+1)。但是,如果你收到一个+1保留计数的自我,并且你自己保留释放,你可以确定它通过你的-init方法保持活着,并且你和你一个人拥有self。如果给定的自己不是“活着的”,那么你可以保证在释放中间返回nil而不是一个对象。因此,上述变为(在伪C中):

id init(__attribute((ns_consumed))id self, SEL _cmd) {
    //implicit retain of +1 self returns self with +2
    //...
    //implicit release of +2 self returns self with +1
}

我喜欢将这种模式称为“老式原子访问”,它用于在@synchronized {}原子吸气剂发明之前设计的Apple框架中。当您使用支持公共iVar的getter时。您经常会看到遵循该模式的方法,如:

- (NSView *)view {
    //explicit retain-autorelease of +1 variable is +2 -> +1, guaranteed access or nil.
    return [[_view retain]autorelease];
}

但所有这些都不会触及-init和家庭例外的所有权规则。 -init方法返回对象+1,但谁拥有它们?好吧,分配器*仍然可以参与变量,self = [super init] doesn't actually retain anything(并且它还必须服从整个“返回+1”规则)。好吧,我再次转向伪代码,但这一次它将在Objective-C:

- (id)init {
    //self is +1 because -init must return +1
    self = [super init];
    //implicit [self retain]; leaves self with a +2 reference count
    //...
    //implicit [self autorelease]; leaves self with a +1 reference count
    return self;
}

好的,所以现在你的分配器声称有一个+1对象浮动,那么如何“声明”它?当然是作业!隐式__strong本地和__strong属性的要点是从分配器实体中回收对象。

- (void)f {
    //Freestanding +1 variable is not owned by the caller, will be deallocated when the method
    //passes out of scope.
    [[NSObject alloc]init];

    //Implicitly __strong local captures reference away from allocator entity, 
    //which "autoreleases" it's ownership
    //We now "own" the returned object.
    NSObject *obj = [[NSObject alloc]init];

    //Strong property captures reference away from local, saves the object from being deallocated
    //when the method passes out of scope
    self.object = obj;
}

* Allocator,在本答案的上下文中,指的是最终调用malloc()为对象分配空间的函数。