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

Why rename synthesized properties in iOS with leading underscores?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
How does an underscore in front of a variable in a cocoa objective-c class work?

在xcode 4中创建新项目时,样板代码在实现文件中合成ivar时添加下划线字符,如下所示:

1
@synthesize window = _window;

或:

1
@synthesize managedObjectContext = __managedObjectContext;

有人能告诉我这里正在做什么吗?我不是一个完美无缺的人,但这是客观C的一个方面,我不明白。

另一个困惑点是:在应用程序委托实现中,在如上所述合成窗口ivar后,在应用程序didFinishLaunchingWithOptions:方法中,使用self引用窗口和viewController ivar:

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

但在dealloc方法中,它是窗口,或视图控制器

谢谢


这是Objective-C运行时早期版本的工件。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface Foo : Bar {
  Baz *_qux;
}

@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

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

@end

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

我们直接在-dealloc中使用实例变量;相反,使用accessor方法会如下所示(尽管我不建议这样做,因为我将很快解释一下原因):

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

这有释放qux的效果,并将参考归零。但这可能会产生不幸的副作用:

  • 您最终可能会触发一些意外的通知。其他对象可能正在观察qux的变化,当使用访问器方法更改它时会记录这些变化。
  • (并非每个人都同意这一点:)将指针归零,因为访问器会隐藏程序中的逻辑错误。如果在对象解除分配后访问对象的实例变量,则说明您做了严重错误的事情。但是,由于Objective-C的nil消息传递语义,您永远不会知道,使用了访问器将设置为nil。如果您直接释放实例变量而不将引用归零,那么访问释放的对象将导致一个很大的EXC_BAD_ACCESS

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

1
2
3
4
5
6
7
8
9
10
11
12
13
@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:访问。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
@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])和self.qux = blah;([self setQux:blah])。

令人遗憾的是,苹果的示例代码和模板代码太差劲了。不要将它用作正确的Objective-C风格的指南,也不要将它用作正确的软件架构的指南。:)


这是另一个原因。在不强调实例变量的情况下,您经常使用参数self.title = titleself.rating = rating获得警告:

1
2
3
4
5
6
7
8
9
10
11
@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

您可以通过突出显示实例变量来避免警告:

1
2
3
4
5
6
7
8
9
10
11
@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


in the application didFinishLaunchingWithOptions: method the window and viewController ivars are referred to using self

不,他们不是。这些是对windowviewController属性的引用。这就是下划线的要点,以便在使用属性(无下划线)和直接访问ivar(带下划线)时更加清楚。


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