关于C#:在init和dealloc方法中有效使用访问器?

Valid use of accessors in init and dealloc methods?

我现在从几个来源(stackoverflow.com,cocoa-dev,文档,博客等)中听到,在init和dealloc方法中使用访问器和设置(foo,setFoo :)是"错误的"。据我所知,如果你这样做,很可能会混淆其他正在观察财产的物体。 (这里给出一个简单的例子)

但是,由于以下原因,我不得不说我不同意这种做法:

新的Objective-C运行时(iPhone上的那个和10.5中的64位运行时)允许您在不声明相应的ivar的情况下声明属性。例如,以下类将在10.5或iPhone(设备,而不是模拟器)上编译得很好:

1
2
3
4
5
6
7
8
9
10
11
@interface Foo : NSObject { }

  @property (retain) id someObject;

@end

@implementation Foo

  @synthesize someObject;

@end

理解上面是一个完全有效的Objective-C类,假设我决定编写一个初始化程序,并且出于内存管理的目的,使用dealloc方法(因为在iPhone上没有GC)。我读过有关初始化器和释放器的所有内容都会让我编写以下两种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (id) init {
  if (self = [super init]) {
    //initialize the value of someObject to nil
    [self setSomeObject:nil];
  }
  return self;
}

- (void) dealloc {
  //setting someObject to nil will release the previous value
  [self setSomeObject:nil];
  [super dealloc];
}

但是,根据文件和流行的观点,这是"错误的"。所以我的问题是这样的:

  • 如何在不使用访问器的情况下初始化someObject?您可能会说编译器(或运行时或其他)将确保someObject已设置为nil,但我相信依赖它会是不正确的行为。在C语言中有一个不错的背景,由于没有正确初始化变量,我看到了相当数量的错误,这似乎没有什么不同。
  • 如果我不应该在dealloc方法中使用访问器,我该如何释放someObject?
  • 如果其中任何一个的答案是"你不能",那么在init和dealloc方法中使用访问器怎么可能不好?


    编辑(2013年2月13日):正如我在下面的评论中所指出的那样,特别是在增加ARC之后,我已经改变了主意。在ARC之前,由于init中的ivar分配不正确,我看到了很多导致崩溃的错误。 IMO,特别是与初级团队合作,使用init中的访问器的罕见问题被ivar访问的常见错误所抵消。由于ARC消除了这些类型的错误,使用init中的访问器可能导致的罕见但可能的错误更重要,因此我转而支持在initdealloc中直接使用ivars ,只在那些地方;其他地方的访问器是可能的(显然你不能在访问器本身内部使用访问器....)

    PRE-ARC回答

    我强烈反对那些反对-init中的访问者的人。几乎在所有情况下,这都是使用访问器的一个非常好的地方,并且它节省了我在新的Cocoa编码器中看到的很多错误,这些错误在-init中分配时总是无法保留。

    -dealloc是一个更强硬的电话。我有自然倾向于在那里使用访问器(以便它们在任何地方使用),但它可能会因KVO而导致头痛(如果你在你的setter中发布更改通知,甚至可能会导致NSNotifications)。也就是说,虽然我不在-dealloc中使用访问器,但我认为这是非常有争议的,Apple对此非常不一致(我们知道他们在UIViewController的-dealloc中调用setView:)。

    在任何情况下,我都会说访问器的使用不足已导致100倍的过度使用错误。我总是错误地使用它们,除非有充分的理由不这样做。


    据我所知,合成的ivars不能直接访问的当前10.5行为被Apple认为是一个bug;你应该能够直接访问它,但不能。

    因此,你应该能够做到:

    1
    someObject = nil;

    代替

    1
    self.someObject = nil;

    与此同时,直接使用访问器是唯一的方法,无需提供明确的ivar。

    更新:此错误已得到修复;你现在可以做someObject = nil就好了。