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

Alp*_*sta 126 coding-style objective-c ios4

可能重复:
cocoa objective-c类中变量前面的下划线如何工作?

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

@synthesize window = _window;
Run Code Online (Sandbox Code Playgroud)

要么:

@synthesize managedObjectContext = __managedObjectContext;
Run Code Online (Sandbox Code Playgroud)

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

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

self.window.rootViewController = self.viewController
[self.window makeKeyAndVisible];
Run Code Online (Sandbox Code Playgroud)

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

谢谢

Jon*_*ing 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
Run Code Online (Sandbox Code Playgroud)

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

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

- (void)dealloc {
  self.qux = nil; // [self setQux:nil];
  [super dealloc];
}
Run Code Online (Sandbox Code Playgroud)

这具有释放qux以及将参考归零的效果.但这可能会产生不幸的副作用:

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

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

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

@implementation Foo
@synthesize qux = _qux;

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

@end
Run Code Online (Sandbox Code Playgroud)

这实际上合成上的一个实例变量Foo调用_qux,这是由吸气剂和setter消息访问-qux-setQux:.

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

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

@implementation Foo
@synthesize qux;

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

@end
Run Code Online (Sandbox Code Playgroud)

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

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

  • '@synthesize quz = _quz;`有一个很好的理由.当你的意思是'self.quz`时它会意外地写'quz`,反之亦然.编译器问题相对较短,但确实存在.如果你找到了borked的例子,请提交bug. (59认同)
  • 我很乐意看到这个答案更新了"启用ARC"项目的建议/后果. (11认同)
  • 你在哪里得到"Apple不希望你使用下划线?".它仅用于保留下划线前缀的方法,而不是变量. (4认同)
  • 我只是使用@synthesize quz = quz_,因此我可以从错误中拯救自己,而不必考虑Apple关于避免使用下划线前缀的建议.我也倾向于使用Jeff LaMarche的MCRelease宏,如下所述:[iPhone开发:Dealloc](http://bit.ly/emmVIP),出于他陈述的所有原因. (3认同)

Fre*_*man 13

这是另一个原因.在没有强调实例变量的情况下,您经常会使用参数获得警告,self.title = title并且self.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
Run Code Online (Sandbox Code Playgroud)

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

@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
Run Code Online (Sandbox Code Playgroud)


LaC*_*LaC 6

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

不,他们不是.这些都是以引用的属性 windowviewController.这是下划线的要点,使得在使用属性时更清晰(没有下划线)以及直接访问ivar(使用下划线).