Objective c ARC中指针所有权问题的处理

Objective c ARC中指针所有权问题的处理,objective-c,automatic-ref-counting,Objective C,Automatic Ref Counting,假设对象A有一个属性: @property (nonatomic, strong) Foo * bar; 在实施中综合如下: @synthesize bar = _bar; 对象B操纵一个Foo**,如本例中来自对象a的调用: Foo * temp = self.bar; [objB doSomething:&temp]; self.bar = temp; 这样做或类似的事情是否合法 doSomething:方法的正确声明是什么 此外,假设对象B可能在我有机会设置条属性之前被解

假设对象A有一个属性:

@property (nonatomic, strong) Foo * bar;
在实施中综合如下:

@synthesize bar = _bar;
对象B操纵一个
Foo**
,如本例中来自对象a的调用:

Foo * temp = self.bar;
[objB doSomething:&temp];
self.bar = temp;
  • 这样做或类似的事情是否合法
  • doSomething:
    方法的正确声明是什么
此外,假设对象B可能在我有机会设置
属性之前被解除分配(从而获得
temp
指向的实例的所有权)-我如何告诉ARC放弃拥有的引用?换句话说,如果我想让下面的示例代码段工作,我需要如何处理ARC问题

Foo * temp = self.bar;    // Give it a reference to some current value
[objB doSomething:&temp]; // Let it modify the reference
self.bar = nil;           // Basically release whatever we have
_bar = temp;              // Since we're getting back an owning reference, bypass setter
  • 我在想什么
编辑

根据@KevinBallard的回答,我只想确认我的理解。这是正确的吗

对象A:

@implementation ObjectA

@synthesize bar = _bar;

- (void)someMethod
{
    ObjectB * objB = [[ObjectB alloc] initWithFoo:&_bar];
    // objB handed off somewhere and eventually it's "doSomething" method is called.
}

@end
@implementation ObjectB
{
    Foo * __autoreleasing * _temp;
}

- (id)initWithFoo:(Foo * __autoreleasing *)temp
{
    id self = [super init];
    if (self)
    {
        _temp = temp;
    }
    return self;
}

- (void)doSomething
{
    ...
    *_temp = [[Foo alloc] init]; 
    ...
}

@end
对象B:

@implementation ObjectA

@synthesize bar = _bar;

- (void)someMethod
{
    ObjectB * objB = [[ObjectB alloc] initWithFoo:&_bar];
    // objB handed off somewhere and eventually it's "doSomething" method is called.
}

@end
@implementation ObjectB
{
    Foo * __autoreleasing * _temp;
}

- (id)initWithFoo:(Foo * __autoreleasing *)temp
{
    id self = [super init];
    if (self)
    {
        _temp = temp;
    }
    return self;
}

- (void)doSomething
{
    ...
    *_temp = [[Foo alloc] init]; 
    ...
}

@end

这将创建一个编译时错误:
将非本地对象的地址传递给回写的自动删除参数

这是完全合法的。财产准入不相关;向对象传递指针通常是通过
NSError*
对象完成的

声明方法的正确方法是

- (returntype)doSomething:(Foo * __autoreleasing *)arg;
这将其声明为指向
\uu autorelease
对象的指针,这基本上意味着所指向的对象被假定为
-autorelease
d

至于“此外”,这不是ARC下的问题。你的线路

Foo * temp = self.bar;
相当于

__strong Foo *temp = self.bar;
我希望这对您来说很明显,这使得
temp
成为一个强引用,因此只要变量存在,它就“拥有”它的值。换句话说,你可以说

Foo *temp = self.bar;
self.bar = nil;

并且
temp
仍然有效。

ARC需要知道对象引用的所有权,以便能够确定何时释放它等。对于任何变量(本地、实例或全局),ARC都有确定所有权的规则;通过推理或显式属性。这相当于程序员跟踪所有权的弧前需求

但是如果你有一个变量的引用,会发生什么呢?您无法(预ARC)自己编写接受变量引用的代码,并且无论该变量的所有权如何,这些代码都能正常工作,因为您不知道是否需要释放等。也就是说,您无法构造适用于未知所有权的变量(在变化的意义上!)的代码

ARC面临同样的问题,它的解决方案是推断或接受一个明确的属性,指定被引用变量的所有权,然后要求调用方安排传递对具有适当所有权的变量的引用。后一位可能需要使用隐藏的临时变量。这在中被称为“最小错误解决方案”,并被称为“通过写回传递”

问题的第一部分:

  • 这样做或类似的事情是否合法
是的,代码可以用圆弧表示
temp
被推断为
strong
,一些幕后人员碰巧通过引用
doSomething:
传递了它

  • doSomething:方法的正确声明是什么
ARC推断
ByRefoo
属于类型
Foo*\uuu autoreleasing*
-对自动删除引用的引用。这就是“通过写回传递”所要求的

此代码仅有效,因为
temp
是本地代码。使用实例变量执行此操作是不正确的(正如您在编辑中发现的)。如果参数在标准“输出”模式下使用,并且在
doSomething:
返回时分配了任何更新的值,则该参数也仅有效。这两种情况都是因为旁路写回方式是“最不坏的解决方案”的一部分

摘要:当使用局部变量时,它们可以通过引用传递,用于标准的“输出”模式,并推断出任何必需的属性等

幕后 我们将使用类型
Breadcrumbs
,而不是问题的
Foo
;这本质上是一个包装的
NSString
,它跟踪每个
init
retain
release
autorelease
dealloc
(几乎如下文所示),这样我们就可以看到发生了什么。面包屑的书写方式并不重要

现在考虑下面的类:

@implementation ByRef
{
   Breadcrumbs *instance;                                // __strong inferred
}
更改通过引用传递的值的方法:

- (void) indirect:(Breadcrumbs **)byRef                  // __autoreleasing inferred
{
   *byRef = [Breadcrumbs newWith:@"banana"];
}
一个用于
间接:
的简单包装器,以便我们可以看到它传递了什么以及何时返回:

- (void) indirectWrapper:(Breadcrumbs **)byRef           // __autoreleasing inferred
{
   NSLog(@"indirect: passed reference %p, contains %p - %@, owners %lu", byRef, *byRef, *byRef, [*byRef ownerCount]);
   [self indirect:byRef];
   NSLog(@"indirect: returned");
}
以及一种演示对局部变量调用的
间接:
(想象地调用
local
)的方法:

现在,使用一些代码来练习
demo1
本地化自动释放池,以便我们可以查看分配、释放的内容以及何时:

ByRef *test = [ByRef new];

NSLog(@"Start demo1");
@autoreleasepool
{
   [test demo1];
   NSLog(@"Flush demo1");
}
NSLog(@"End demo1");
在控制台上执行上述操作将产生以下结果:

ark[2041:707] Start demo1
ark[2041:707] Strong local passed by autoreleasing reference
ark[2041:707] >>> 0x100176f30: init
ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100176f30 - apple, owners 1
ark[2041:707] indirect: passed reference 0x7fff5fbfedb8, contains 0x100176f30 - apple, owners 1
ark[2041:707] >>> 0x100427d10: init
ark[2041:707] >>> 0x100427d10: autorelease
ark[2041:707] indirect: returned
ark[2041:707] >>> 0x100427d10: retain
ark[2041:707] >>> 0x100176f30: release
ark[2041:707] >>> 0x100176f30: dealloc
ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100427d10 - banana, owners 2
ark[2041:707] >>> 0x100427d10: release
ark[2041:707] Flush demo1
ark[2041:707] >>> 0x100427d10: release
ark[2041:707] >>> 0x100427d10: dealloc
ark[2041:707] End demo1
[这些“>>>”行来自
Breadcrumbs
]只需跟随对象(0x100…)和变量(0x7fff…)的地址,就可以了

也许不是!在这里,每个块后面都有注释:

ark[2041:707] Start demo1
ark[2041:707] Strong local passed by autoreleasing reference
ark[2041:707] >>> 0x100176f30: init
ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100176f30 - apple, owners 1
这里我们看到
[Breadcrumbs newWith:@“apple”]
在地址
0x100176f30
处创建了一个对象。这存储在
local
中,其地址为
0x7fff5fbfedc0
,并且对象有一个所有者(
local

隐藏变量来了:as
间接:
需要对自动释放变量的引用。ARC创建了一个新变量,其地址为
0x7fff5fbfedb8
,并将对象引用(
0x100176f30
)复制到该变量中

ark[2041:707] >>> 0x100427d10: init
ark[2041:707] >>> 0x100427d10: autorelease
ark[2041:707] indirect: returned
内部
间接:
创建一个新对象并自动删除
ark[2041:707] indirect: passed reference 0x7fff5fbfedb8, contains 0x100176f30 - apple, owners 1
ark[2041:707] >>> 0x100427d10: init
ark[2041:707] >>> 0x100427d10: autorelease
ark[2041:707] indirect: returned
ark[2041:707] >>> 0x100427d10: retain
ark[2041:707] >>> 0x100176f30: release
ark[2041:707] >>> 0x100176f30: dealloc
ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100427d10 - banana, owners 2
ark[2041:707] >>> 0x100427d10: release
ark[2041:707] Flush demo1
ark[2041:707] >>> 0x100427d10: release
ark[2041:707] >>> 0x100427d10: dealloc
ark[2041:707] End demo1
- (void) strongIndirect:(Breadcrumbs * __strong *)byRef
{
   *byRef = [Breadcrumbs newWith:@"plum"];
}

- (void) strongIndirectWrapper:(Breadcrumbs * __strong *)byRef
{
   NSLog(@"strongIndirect: passed reference %p, contains %p - %@, owners %lu", byRef, *byRef, *byRef, [*byRef ownerCount]);
   [self strongIndirect:byRef];
   NSLog(@"strongIndirect: returned");
}
- (void) demo2
{
   NSLog(@"Strong instance passed by strong reference");
   instance = [Breadcrumbs newWith:@"orange"];
   NSLog(@"instance: addr %p, contains %p - %@, owners %lu", &instance, instance, instance, [instance ownerCount]);
   [self strongIndirectWrapper:&instance];
   NSLog(@"instance: addr %p, contains %p - %@, owners %lu", &instance, instance, instance, [instance ownerCount]);
}
1  ark[2041:707] Start demo2
2  ark[2041:707] Strong instance passed by strong reference
3  ark[2041:707] >>> 0x100176f30: init
4  ark[2041:707] instance: addr 0x100147518, contains 0x100176f30 - orange, owners 1
5  ark[2041:707] strongIndirect: passed reference 0x100147518, contains 0x100176f30 - orange, owners 1
6  ark[2041:707] >>> 0x100427d10: init
7  ark[2041:707] >>> 0x100176f30: release
8  ark[2041:707] >>> 0x100176f30: dealloc
9  ark[2041:707] strongIndirect: returned
10 ark[2041:707] instance: addr 0x100147518, contains 0x100427d10 - plum, owners 1
11 ark[2041:707] Flush demo2
12 ark[2041:707] End demo2
- (void) demo3
{
   NSLog(@"Strong local passed by strong reference");
   Breadcrumbs *local;                                   // __strong inferred
   local = [Breadcrumbs newWith:@"apple"];
   NSLog(@"local: addr %p, contains %p - %@, owners %lu", &local, local, local, [local ownerCount]);
   [self strongIndirectWrapper:&local];
   NSLog(@"local: addr %p, contains %p - %@, owners %lu", &local, local, local, [local ownerCount]);
}
1  ark[2041:707] Start demo3
2  ark[2041:707] Strong local passed by strong reference
3  ark[2041:707] >>> 0x100176f30: init
4  ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100176f30 - apple, owners 1
5  ark[2041:707] strongIndirect: passed reference 0x7fff5fbfedc0, contains 0x100176f30 - apple, owners 1
6  ark[2041:707] >>> 0x100427d20: init
7  ark[2041:707] >>> 0x100176f30: release
8  ark[2041:707] >>> 0x100176f30: dealloc
9  ark[2041:707] strongIndirect: returned
10 ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100427d20 - plum, owners 1
11 ark[2041:707] >>> 0x100427d20: release
12 ark[2041:707] >>> 0x100427d20: dealloc
13 ark[2041:707] Flush demo3
14 ark[2041:707] End demo3
- (void) demo4
{
   NSLog(@"Weak local passed by autoreleasing reference");
   instance = [Breadcrumbs newWith:@"peach"];
   Breadcrumbs __weak *weakLocal = instance;
   NSLog(@"weakLocal: addr %p, contains %p - %@, owners %lu", &weakLocal, weakLocal, weakLocal, [weakLocal ownerCount]);
   [self indirectWrapper:&weakLocal];
   NSLog(@"weakLocal: addr %p, contains %p -, %@, owners %lu", &weakLocal, weakLocal, weakLocal, [weakLocal ownerCount]);
}
 1 ark[2041:707] Start demo4
 2 ark[2041:707] Weak local passed by autoreleasing reference
 3 ark[2041:707] >>> 0x608000000d10: init
 4 ark[2041:707] weakLocal: addr 0x7ffeefbfde58, contains 0x608000000d10 - peach, owners 4
 5 ark[2041:707] >>> 0x608000000d10: retainWeakReference
 6 ark[2041:707] indirect: passed reference 0x7ffeefbfde40, contains 0x608000000d10 - peach, owners 2
 7 ark[2041:707] >>> 0x604000001060: init
 8 ark[2041:707] >>> 0x604000001060: autorelease
 9 ark[2041:707] indirect: returned
10 ark[2041:707] >>> 0x608000000d10: release
11 ark[2041:707] weakLocal: addr 0x7ffeefbfde58, contains 0x604000001060 - banana, owners 4
12 ark[2041:707] Flush demo4
13 ark[2041:707] >>> 0x604000001060: release
14 ark[2041:707] >>> 0x604000001060: dealloc
15 ark[2041:707] End demo4
16 ark[2041:707] >>> 0x608000000d10: release
17 ark[2041:707] >>> 0x608000000d10: dealloc