首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么在iOS中重命名带有前导下划线的合成属性?

为什么在iOS中重命名带有前导下划线的合成属性?
EN

Stack Overflow用户
提问于 2011-03-29 08:11:14
回答 4查看 40.3K关注 0票数 126

可能重复:

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

在Xcode4中创建新项目时,样板代码在合成实现文件中的ivars时会添加一个下划线字符,如下所示:

代码语言:javascript
复制
@synthesize window = _window;

或者:

代码语言:javascript
复制
@synthesize managedObjectContext = __managedObjectContext;

有人能告诉我这里正在完成什么吗?我不是一个十全十美的人,但这是objective-C的一个方面,我不明白。

另一个令人困惑的地方;在应用程序委托实现中,在像上面那样合成窗口iVar之后,在应用程序didFinishLaunchingWithOptions: method中,使用self引用窗口和viewController iVar:

代码语言:javascript
复制
self.window.rootViewController = self.viewController
[self.window makeKeyAndVisible];

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

谢谢

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-03-29 08:29:34

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

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

代码语言:javascript
复制
@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发送给object self的消息。

我们直接在-dealloc中使用实例变量;改用访问器方法将如下所示(尽管我不推荐这样做,原因我稍后会解释):

代码语言:javascript
复制
- (void)dealloc {
  self.qux = nil; // [self setQux:nil];
  [super dealloc];
}

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

  • 你可能最终会触发一些意想不到的通知。其他对象可能正在观察qux的更改,这些更改是在使用访问器方法更改它时记录的。
  • (并不是每个人都同意这一点:)像访问器一样将指针置零可能会隐藏程序中的逻辑错误。如果你在对象被释放之后访问对象的实例变量,那么你正在做一些严重错误的事情。然而,由于Objective-C的nil-messaging语义,您永远不会知道,因为使用了访问器将其设置为nil。如果您直接释放实例变量,而不是将引用置零,那么访问释放的对象将导致响亮的EXC_BAD_ACCESS.

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

代码语言:javascript
复制
@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。如果您认为您可以相信自己能够记住您使用的是原始实例变量还是访问器方法,那么只需像这样做:

代码语言:javascript
复制
@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])。

令人遗憾的是,Apple的示例代码和模板代码都很糟糕。永远不要把它作为正确的Objective-C风格的指南,当然也不要把它作为正确的软件架构的指南。:)

票数 223
EN

Stack Overflow用户

发布于 2012-05-07 23:17:59

这是另一个原因。如果不对实例变量加下划线,则经常会收到有关参数self.title = titleself.rating = rating的警告

代码语言:javascript
复制
@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

您可以通过在实例变量下划线来避免警告:

代码语言:javascript
复制
@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
票数 13
EN

Stack Overflow用户

发布于 2011-03-29 08:25:07

应用程序didFinishLaunchingWithOptions中的

:方法使用自身引用窗口和viewController ivars

不,他们不是。这些是对属性windowviewController的引用。这就是下划线的意义所在,当属性正在被使用时(没有下划线),以及当ivar被直接访问时(带下划线),它变得更加清晰。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5466496

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档