7

我想了解 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;
4

2 回答 2

8

所以 alloc 返回一个保留计数为 1 的对象,对吧?现在在进入init之前又被保留,然后在init结束时释放,所以我们又回到了1。为什么要这样设计呢?

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

当我想到典型的初始化是什么样子时,我会更加困惑。

可以肯定的是,这种行为可以涵盖看起来不像典型 -init方法的情况。修改self并不完全典型,它只是允许的。

于 2013-06-21T21:28:06.160 回答
4

为什么不应该这样呢?调用-init旨在返回具有 +1 保留计数的给定对象,并且执行“临时保留”是保证 self 在整个给定 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带有 +0 保留计数的 a(这在大多数保留其参数的方法中很常见,即 setter),那么您必须保证在继承链的某处有人足够好,可以让 self 远离分配它的任何事情(最终self有一个相当模糊的保留计数+1)。但是,如果您收到一个具有 +1 保留计数的 self,并且您自己执行了一个保留释放它,您可以通过您的-init方法确定它仍然存在,并且您和您独自拥有self. 如果给定的 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 {}原子 getter 发明之前设计的 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] 实际上并没有保留任何东西(而且它还必须遵守整个“返回 +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;
}

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

于 2013-06-21T19:01:54.637 回答