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

时间:2011-03-29 00:11:14

标签: objective-c ios4 coding-style

  

可能重复:
  How does an underscore in front of a variable in a cocoa objective-c class work?

在Xcode 4中创建新项目时,样板代码在将实体文件中的ivars合成为时会添加下划线字符:

@synthesize window = _window;

或:

@synthesize managedObjectContext = __managedObjectContext;

有人能告诉我这里有什么成就吗?我不是一个完整的润滑剂,但这是客观的一个方面 - 我不明白。

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

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

但是在dealloc方法中它是_window或_viewController

由于

4 个答案:

答案 0 :(得分:223)

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

最初,@synthesize用于创建访问器方法,但运行时仍然要求必须明确实例化实例变量:

@interface Foo : Bar {
  Baz *_qux;
}

@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

- (void)dealloc {
  [_qux release];
  [super dealloc];
}

@end

人们会在实例变量前加上前缀,以区别于他们的属性(即使Apple不希望你使用下划线,但这是另一回事)。您合成属性以指向实例变量。但重点是,_qux是一个实例变量,而self.qux(或[self qux])是发送给对象qux的消息self

我们直接在-dealloc中使用实例变量;使用访问器方法会看起来像这样(虽然我不推荐它,原因我很快就会解释):

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

这具有释放qux的效果,以及将引用清零。但这可能会产生不幸的副作用:

  • 您最终可能会发出一些意外通知。其他对象可能正在观察对qux的更改,这些更改是在使用访问器方法进行更改时记录的。
  • (并非所有人都同意这一点:)将指针清零作为访问者可能会隐藏程序中的逻辑错误。如果您在 之后访问对象的实例变量 ,则该对象已被解除分配,您正在做一些严重错误的事情。但是,由于Objective-C的nil - 消息语义,你永远不会知道,使用了访问器设置为nil。如果您直接释放了实例变量而没有将引用清零,则访问已解除分配的对象会导致响亮EXC_BAD_ACCESS

除了访问器方法之外,运行时的更高版本还添加了合成实例变量的功能。使用这些版本的运行时,可以编写上面的代码省略实例变量:

@interface Foo : Bar
@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

- (void)dealloc {
  [_qux release];
  [super dealloc];
}

@end

这实际上合成了Foo上名为_qux的实例变量,该变量由getter和setter消息-qux-setQux:访问。

我建议不要这样做:它有点乱,但有一个很好的理由使用下划线;即,防止意外地直接进入伊达尔。如果你认为你可以相信自己要记住你是使用原始实例变量还是访问器方法,那就这样做吧:

@interface Foo : Bar
@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux;

- (void)dealloc {
  [qux release];
  [super dealloc];
}

@end

然后,当您想直接访问实例变量时,只需说qux(在C语法中转换为self->qux以便从指针访问成员)。当你想使用访问器方法(它将通知观察者,做其他有趣的事情,并使内存管理更安全,更容易)时,使用self.qux[self qux])和{{1} }(self.qux = blah;)。

令人遗憾的是,Apple的示例代码和模板代码很糟糕。切勿将其用作正确Objective-C风格的指南,当然也不要将其用作正确软件架构的指南。 :)

答案 1 :(得分:13)

这是另一个原因。在没有强调实例变量的情况下,您经常会使用参数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

通过强调实例变量来避免警告:

@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

答案 2 :(得分:6)

  

在应用程序中的didFinishLaunchingWithOptions:方法窗口和viewController ivars是使用self引用的

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

答案 3 :(得分:2)

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