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

这个问题在这里已经有了答案:
关闭10年前.

Possible Duplicate:
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;

谁能告诉我这里正在完成什么?我不是一个完整的 nube,但这是我不理解的 Objective-C 的一个方面。

另一个混淆点;在app delegate实现中,如上合成window iVar后,在应用didFinishLaunchingWithOptions:方法中,window和viewController ivars使用self来引用:

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

但在 dealloc 方法中它是 _window 或 _viewController

谢谢

最佳答案

这是以前版本的 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:.

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

@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 风格的指南,当然也永远不要将其用作正确的软件架构的指南。 :)

https://stackoverflow.com/questions/5466496/

相关文章:

objective-c - RootViewController 切换过渡动画

objective-c - 检测 NSString 是否包含...?

objective-c - 使用 ARC 时是否在 dealloc 中将属性设置为 nil?

objective-c - NSInteger 的 NSLog/printf 说明符?

iphone - 如何自动调整 UIScrollView 的大小以适合其内容

ios - 以编程方式触发按钮单击事件?

iphone - iPhone 上 NSString 的 AES 加密

ios - 如何比较 UIColors?

ios - SDK iOS 8.0 中的产品类型单元测试包需要代码签名

iphone - 如何获取 UIImage 并给它一个黑色边框?