为什么在iOS中使用前导下划线重新命名合成的属性?

可能重复:
如何在cocoaObjective-C类中的variables前面的下划线工作?

在Xcode 4中创build新项目时,样板代码在实现文件中将ivars综合为以下内容时会添加一个下划线字符:

@synthesize window = _window; 

要么:

 @synthesize managedObjectContext = __managedObjectContext; 

有人能告诉我在这里做了什么? 我不是一个完整的nube,但这是我不明白的目标-C的一个方面。

另一个混乱点; 在应用程序委托实现中,在合成如上所述的窗口iVar之后,在应用程序didFinishLaunchingWithOptions:方法中,窗口和viewController ivars使用self:

 self.window.rootViewController = self.viewController [self.window makeKeyAndVisible]; 

但在dealloc方法是_window或_viewController

谢谢

这是以前版本的Objective-C运行时的工件。

最初,@ @synthesize被用来创build访问器方法,但是运行时仍然需要显式地实例化实例variables:

 @interface Foo : Bar { Baz *_qux; } @property (retain) Baz *qux; @end @implementation Foo @synthesize qux = _qux; - (void)dealloc { [_qux release]; [super dealloc]; } @end 

人们会在他们的实例variables前加上区分他们的属性(即使苹果公司不希望你使用下划线,但这是另外一回事)。 您将该属性综合为指向该实例variables。 但重点是, _qux是一个实例variables, self.qux (或[self qux] )是发送给对象self的消息qux

我们直接在-dealloc使用实例variables; 使用访问器方法,而不是看起来像这样(虽然我不build议,因为我会很快解释):

 - (void)dealloc { self.qux = nil; // [self setQux:nil]; [super dealloc]; } 

这具有释放qux的效果,以及清零参考的function。 但是这可能会有不幸的副作用:

  • 你可能最终会发射一些意外的通知。 其他对象可能正在观察对qux更改,这些更改会在使用访问器方法来更改时进行logging。
  • (不是每个人都同意这一点:)清零指针作为访问者可能会隐藏您的程序中的逻辑错误。 如果你在对象被释放访问一个对象的实例variables,那么你正在做一些严重错误的事情。 但是,由于Objective-C的nil -messaging语义,你永远不会知道,使用访问器设置为nil 。 如果你直接释放了实例variables,而不是将引用清零,那么访问释放的对象会导致一个巨大的EXC_BAD_ACCESS

运行时的更新版本增加了除访问器方法之外的综合实例variables的function。 使用这些版本的运行时,上面的代码可以写成省略实例variables:

 @interface Foo : Bar @property (retain) Baz *qux; @end @implementation Foo @synthesize qux = _qux; - (void)dealloc { [_qux release]; [super dealloc]; } @end 

这实际上在Foo上合成了一个名为_qux的实例variables,可以通过getter和setter消息-qux-setQux:

我build议不要这样做:有点混乱,但是有一个很好的理由使用下划线; 即防止意外直接接触伊娃。 如果你认为你可以相信自己要记住你使用的是原始实例variables还是访问方法,只需要这样做:

 @interface Foo : Bar @property (retain) Baz *qux; @end @implementation Foo @synthesize qux; - (void)dealloc { [qux release]; [super dealloc]; } @end 

然后,当你想直接访问实例variables时,只需说qux (在C语法中用于从一个指针访问一个成员就转换为self->qux )。 当你想要使用访问器方法(它会通知观察者,做其他有趣的事情,并使内存pipe理更安全,更容易),使用self.qux[self qux] )和self.qux = blah;[self setQux:blah] )。

可悲的是,苹果的示例代码和模板代码很糟糕。 切勿将其用作正确的Objective-C风格的指南,当然不要将其用作正确的软件体系结构的指南。 🙂

这是另一个原因。 如果不强调实例variables,您经常会获得带有参数self.title = titleself.rating = rating警告:

 @implementation ScaryBugData @synthesize title; @synthesize rating; - (id)initWithTitle:(NSString *)title rating:(float)rating { if (self = [super init]) { self.title = title; // Warning. Local declaration hides instance variable self.rating = rating; // Warning. Local declaration hides instance variable } return self; } @end 

您可以通过强调实例variables来避免警告:

 @implementation ScaryBugData @synthesize title = _title; @synthesize rating = _rating; - (id)initWithTitle:(NSString *)title rating:(float)rating { if (self = [super init]) { self.title = title; // No warning self.rating = rating; // No warning } return self; } @end 

在应用程序didFinishLaunchingWithOptions:方法的窗口和viewController ivars被称为使用自我

不,他们不是。 那些是对属性 windowviewController 。 这是下划线的要点,使用该属性时(无下划线)以及当直接访问ivar(带下划线)时更清晰。

是的,它只是为了区分对象的引用。 也就是说,如果直接引用对象,则使用下划线,否则使用self来引用对象。