【问题标题】:Why rename synthesized properties in iOS with leading underscores? [duplicate]为什么用前导下划线重命名 iOS 中的合成属性? [复制]
【发布时间】:2011-07-24 21:38:57
【问题描述】:

可能重复:
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 ios4 coding-style


    【解决方案1】:

    这是以前版本的 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])是发送给对象self 的消息qux

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

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

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

    • 您最终可能会触发一些意外通知。其他对象可能正在观察对qux 的更改,这些更改会在使用访问器方法对其进行更改时记录下来。
    • (并非所有人都同意这一点:)像访问器那样将指针清零可能会隐藏程序中的逻辑错误。如果您曾经在对象被释放后访问该对象的实例变量,那么您就做错了。然而,由于 Objective-C 的nil-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
    

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

    【讨论】:

    • @synthesize quz = _quz; 有一个很好的理由;当您的意思是self.quz 时,它消除了意外写入quz,反之亦然。编译器问题相对短暂,但确实存在。如果您发现示例乏味,请提交错误。
    • @bbum 好点重新下划线命名。我通常相信自己可以输入正确的内容(或者如果我搞砸了至少要修复它),但这绝对是在设计你的编码风格时需要考虑的事情(我倾向于在美学方面犯错,但它完全有效倾向于防止事故发生)。
    • 我只使用@synthesize quz = quz_,因此我可以避免输入错误,而无需遵循 Apple 关于避免使用下划线前缀的建议。我也倾向于使用 Jeff LaMarche 的 MCRelease 宏,详情如下:iPhone Development: Dealloc,出于他所述的所有原因。
    • 你从哪里得到“Apple 不希望你使用下划线?”。它仅适用于它们保留下划线前缀的方法,而不适用于变量。
    • 我很乐意看到这个答案更新了ARC-enabled 项目的建议/后果。
    【解决方案2】:

    这是另一个原因。如果不使用下划线实例变量,您经常会收到带有参数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
    

    【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2018-04-18
        • 1970-01-01
        • 2014-04-14
        • 2011-07-31
        • 2013-02-21
        • 2013-01-18
        • 2011-09-03
        • 2011-08-28
        相关资源
        最近更新 更多