【问题标题】:Dot notation vs. message notation for declared properties声明属性的点表示法与消息表示法
【发布时间】:2010-11-17 23:25:07
【问题描述】:

我们现在有了属性的“点”符号。我见过各种backforths 关于点符号与消息符号的优点。为了不影响回答,我不会在问题中以任何方式回答。

您对用于属性访问的点表示法与消息表示法有何看法?

请尽量将注意力集中在 Objective-C 上——我要提出的一个偏见是 Objective-C 就是 Objective-C,所以你认为它像 Java 或 JavaScript 的偏好是无效的。

有效的评论与技术问题(操作顺序、转换优先级、性能等)、清晰度(结构与对象性质,优劣!)、简洁性等有关。

请注意,我在代码质量和可读性方面有着严格的学校,曾参与过代码约定和质量至关重要的大型项目(一次写入,读取一千次范例)。

【问题讨论】:

  • +1 这是一个很好的问题,你会得到很多相互矛盾的答案。 =)
  • 源代码编辑器应该根据程序员的个人喜好进行调整,而不改变文件本身的字符。这样每个人都会得到他们喜欢的东西。

标签: objective-c syntax coding-style


【解决方案1】:

不要使用点表示行为。使用点来访问或设置属性之类的东西,通常是声明为属性的属性。

x = foo.name; // good
foo.age = 42; // good

y = x.retain; // bad

k.release; // compiler should warn, but some don't. Oops.

v.lockFocusIfCanDraw; /// ooh... no. bad bad bad

对于刚接触 Objective-C 的人,我建议不要将点用于声明为 @property 的东西之外的任何东西。一旦你对语言有了感觉,就去做感觉对的事情。

例如,我发现以下内容非常自然:

k = anArray.count;
for (NSView *v in myView.subviews) { ... };

您可以期待 clang 静态分析器将增加允许您检查点仅用于某些模式或不用于某些其他模式的能力。

【讨论】:

  • 哇,我什至不知道您可以将点语法用于除属性之外的任何内容。让我感到惊讶! (但我同意,点只能用于属性)
  • 这是不正确的。点表示法是等效方法调用的直接同义词;不多也不少。
  • 我想知道为什么点符号不限于@property 声明的访问器。似乎它允许类作者解决灰色区域,哪些方法应该允许使用点表示法,如果允许类表达它的意图,是否使用像 .uppercaseString 这样的方法就不会那么模棱两可了并由编译器强制执行。也许我缺少一些需要当前实现点表示法的东西。
  • 它可能仅限于@property,但这既需要 API 的使用者学习信息的另一个布尔维度,也需要 API 的设计者很可能淹没在应该和应该什么的争论中不是@property。请注意,Xcode 更喜欢 -- 权重更重 -- @property 在完成代码时隐含的方法......这是鼓励的,但不是强制的。
  • @rodamn 它确实回答了这个问题;它建议仅在语言新手时才将点与 @property 声明的访问器一起使用。随着对 Objective-C 编码的信心增强,做正确的事。有趣的;我们中的许多人已经转向非常有限地使用点。
【解决方案2】:

首先让我说我开始使用 Visual/Real Basic 进行编程,然后转向 Java,所以我相当习惯于点语法。然而,当我终于转向 Objective-C 并习惯了括号,然后看到 Objective-C 2.0 的介绍和它的点语法时,我意识到我真的不喜欢它。 (对于其他语言来说这很好,因为它们就是这样滚动的)。

我在 Objective-C 中有三个主要的点语法问题:

Beef #1: 这让您不清楚为什么会出错。例如,如果我有以下行:

something.frame.origin.x = 42;

然后我会得到一个编译器错误,因为something 是一个对象,你不能使用对象的结构作为表达式的左值。但是,如果我有:

something.frame.origin.x = 42;

然后编译就好了,因为something 本身就是一个结构,它有一个 NSRect 成员,我可以将它用作左值。

如果我采用此代码,我需要花一些时间来弄清楚something 是什么。它是一个结构吗?它是一个对象吗?但是,当我们使用括号语法时,它会更清晰:

[something setFrame:newFrame];

在这种情况下,something 是否是一个对象绝对没有歧义。引入歧义是我的第一个问题。

Beef #2: 在 C 中,点语法用于访问结构的成员,而不是调用方法。程序员可以覆盖对象的setFoo:foo 方法,但仍然可以通过something.foo 访问它们。在我看来,当我看到使用点语法的表达式时,我希望它们是对 ivar 的简单赋值。 情况并非总是如此。考虑一个调节数组和表格视图的控制器对象。如果我打电话给myController.contentArray = newArray;,我希望它会用新数组替换旧数组。然而,最初的程序员可能已经覆盖了setContentArray:,不仅设置了数组,还重新加载了tableview。从这条线上,没有迹象表明这种行为。如果我看到[myController setContentArray:newArray];,我会想“啊哈,一个方法。我需要去看看这个方法的定义,以确保我知道它在做什么。”

所以我认为我对 Beef #2 的总结是你可以用自定义代码覆盖点语法的含义。

3 号牛肉:我认为它看起来很糟糕。作为一名 Objective-C 程序员,我完全习惯于使用括号语法,所以阅读并看到一行一行漂亮的括号,然后突然被foo.name = newName; foo.size = newSize; 等打破,这让我有点分心。我意识到有些东西需要点语法(C 结构),但这是我唯一一次使用它们。

当然,如果您是为自己编写代码,那么请使用您喜欢的任何内容。但是,如果您正在编写计划开源的代码,或者您正在编写一些您不希望永远维护的东西,那么我强烈建议您使用括号语法。当然,这只是我的看法。

最近针对点语法的博文:http://weblog.bignerdranch.com/?p=83

对上述帖子的反驳:http://eschatologist.net/blog/?p=226(原文章支持点语法:http://eschatologist.net/blog/?p=160

【讨论】:

  • 牛肉#1 对我来说似乎有点似是而非。我不认为这是一个问题,就像您可能尝试向结构发送消息然后必须弄清楚该变量实际上是一个结构一样。您是否真的看到很多了解它的工作原理的人在实践中对此感到困惑?这似乎是你曾经搞砸过的那种事情,了解左值的事情,然后在未来做正确的事情——就像,比如说,不要试图将 NSNumbers 视为整数。
  • @Chuck 这在某种程度上是一个边缘案例,但我做了很多合同开发,其中涉及继承项目并不得不使用它们。 通常 something 是一个对象,但我遇到了几个原作者创建结构的地方(为了速度,降低内存使用率等),我不得不花很多钱几分钟试图弄清楚为什么代码不能编译。
【解决方案3】:

我是一个新的 Cocoa/Objective-C 开发人员,我的看法是这样的:

我坚持使用消息符号,即使我从 Obj-C 2.0 开始,即使点符号感觉更熟悉(Java 是我的第一语言。)我这样做的原因很简单:我仍然不'不明白为什么他们将点符号添加到语言中。对我来说,这似乎是一个不必要的“不纯”添加。虽然如果有人能解释它如何使语言受益,我会很高兴听到它。

但是,我认为这是一种风格选择,并且我认为没有正确或错误的方式,只要它是一致且可读的,就像任何其他风格选择(如将左大括号放在与方法标题或下一行相同的行上)。

【讨论】:

  • +1 好答案。我真的很想知道它背后的“为什么”。也许 bbum 或 mmalc 知道答案……(他们都在工作)
【解决方案4】:

Objective-C 点表示法是一种语法糖,它被转换为正常的消息传递,因此在底层没有任何改变,在运行时也没有任何区别。 点符号绝对不会比消息传递快。

在这之后需要一点序言,下面是我看到的利弊:

点符号的优缺点

  • 专业人士

    • 可读性:点符号比嵌套括号更容易阅读
    • 它简化了与属性和属性的交互:对属性使用点表示法,对方法使用消息表示法,您可以在合成器级别实现状态和行为的分离
    • 可以使用复合赋值运算符(1)
    • 使用@property和点符号编译器为你做了很多工作,它可以在获取和设置属性时生成良好的内存管理代码;这就是 Apple 自己的官方指南建议使用点表示法的原因。
  • 缺点

    • 点符号只允许访问声明的@property
    • 由于 Objective-C 是标准 C(语言扩展)之上的一层,点符号并不能真正明确访问的实体是对象还是结构。通常,您似乎正在访问结构的属性。
    • 使用点符号调用方法会失去 named parameters 可读性优势
    • 当混合消息表示法和点表示法看起来像是在用两种不同的语言进行编码时

代码示例:

(1)复合运算符使用代码示例:

//Use of compound operator on a property of an object
anObject.var += 1;
//This is not possible with standard message notation
[anObject setVar:[anObject var] + 1];

【讨论】:

    【解决方案5】:

    使用与语言本身一致的语言风格是最好的建议。但是,这不是在 OO 系统中编写函数代码的情况(反之亦然),点符号是 Objective-C 2.0 语法的一部分。

    任何系统都可能被滥用。在所有基于 C 的语言中,预处理器的存在足以做一些非常奇怪的事情。如果您需要确切了解它有多奇怪,只需查看Obfuscated C Contest。这是否意味着预处理器自动坏了,您永远不应该使用它?

    使用点语法访问已在接口中定义的属性很容易被滥用。潜在的滥用并不一定是反对它的论据。

    属性访问可能有副作用。这与用于获取该属性的语法正交。 CoreData、委托、动态属性(first+last=full)都必须在幕后做一些工作。但这会将“实例变量”与对象的“属性”混淆。没有理由必须按原样存储属性,特别是如果它们可以计算(例如,字符串的长度)。因此,无论您使用 foo.fullName 还是 [foo fullName],仍然会进行动态评估。

    最后,属性的行为(当用作左值时)由对象本身定义,例如是否获取副本或是否保留。这使得以后在属性定义本身中更改行为变得更容易,而不必重新实现方法。这增加了方法的灵活性,从而减少(实施)错误发生的可能性。仍然有可能选择错误的方法(即复制而不是保留),但这是架构问题而不是实现问题。

    最终,它归结为“它看起来像一个结构吗”的问题。这可能是迄今为止辩论的主要区别。如果你有一个结构,它的工作方式与你有一个对象不同。但这一直是真的。您不能向 struct 发送消息,并且您需要知道它是基于堆栈还是基于引用/malloc。已经有一些心理模型在使用方面有所不同([[CGRect alloc] init] 或 struct CGRect?)。它们在行为上从来没有统一过;你需要知道在每种情况下你正在处理什么。为对象添加属性表示不太可能使任何知道其数据类型的程序员感到困惑;如果他们不这样做,他们就会遇到更大的问题。

    至于一致性; (Objective-)C 内部是不一致的。 = 用于赋值和相等,基于源代码中的词法位置。 * 用于指针和乘法。 BOOL 是字符,而不是字节(或其他整数值),尽管 YES 和 NO 分别为 1 和 0。一致性或纯度不是该语言的设计目的。这是为了把事情做好。

    因此,如果您不想使用它,请不要使用它。以不同的方式完成它。如果你想使用它,并且你理解它,你使用它就可以了。其他语言处理通用数据结构(映射/结构)和对象类型(带有属性)的概念,通常对两者使用相同的语法,尽管事实上一种只是数据结构而另一种是丰富的对象。 Objective-C 中的程序员应该具有处理所有风格的编程的同等能力,即使它不是你喜欢的。

    【讨论】:

      【解决方案6】:

      我大多是在 Objective-C 2.0 时代长大的,我更喜欢点符号。对我来说,它可以简化代码,而不是额外的括号,我可以只使用一个点。

      我也喜欢点语法,因为它让我真的感觉就像我正在访问对象的属性,而不是仅仅向它发送消息(当然点语法确实可以翻译进入消息发送,但为了外观,点感觉不同)。与旧语法“调用 getter”不同,感觉就像我直接从对象中获取了一些有用的东西。

      围绕这个问题的一些争论是关于“但我们已经有了点语法,它是为structs!”。这是真的。但是(再一次,这只是心理上的)它对我来说基本上是一样的。使用点语法访问对象的属性感觉与访问结构的成员相同,这或多或少是预期的效果(在我看来)。

      ****编辑:正如 bbum 所指出的,您还可以使用点语法来调用对象上的任何方法(我不知道这一点)。所以我会说我对点语法的看法仅适用于处理对象的属性,而不是日常消息发送**

      【讨论】:

        【解决方案7】:

        我将它用于属性,因为

        for ( Person *person in group.people){ ... }
        

        比阅读更容易

        for ( Person *person in [group  people]){ ... }
        

        在第二种情况下,可读性会因将您的大脑置于消息发送模式而中断,而在第一种情况下,很明显您正在访问 group 对象的 people 属性。

        我在修改集合的时候也会用到,比如:

        [group.people addObject:another_person];
        

        更具可读性
        [[group people] addObject:another_person];
        

        在这种情况下,重点应该是向数组中添加一个对象,而不是链接两条消息。

        【讨论】:

          【解决方案8】:

          我更喜欢消息传递语法...但只是因为那是我学到的。考虑到我的很多课程以及 Objective-C 1.0 风格中没有的课程,我不想混合使用它们。除了“我习惯的”之外,我没有真正的理由不使用点语法......除了这个,这让我发疯了

          [myInstance.methodThatReturnsAnObject sendAMessageToIt]
          

          我不知道为什么,但这真的让我很生气,没有任何理由。我只是觉得这样做

          [[myInstance methodThatReturnsAnObject] sendAMessageToIt]
          

          更具可读性。但对每个人来说都是他自己的!

          【讨论】:

          • 我不时这样做,但前提是我正在访问一个属性。例如:[self.title lowercaseString] 什么的。但我可以从风格上看出为什么混合和匹配语法很麻烦。我这样做的唯一原因是直截了当地了解什么是属性以及什么是返回对象的方法(我知道属性实际上就是这样,但希望您能明白我的意思)。
          【解决方案9】:

          老实说,我认为这归结为风格问题。我个人反对点语法(尤其是在发现您可以将其用于方法调用而不仅仅是读取/写入变量之后)。但是,如果您要使用它,我强烈建议不要将它用于访问和更改变量以外的任何事情。

          【讨论】:

            【解决方案10】:

            面向对象编程的主要优点之一是不能直接访问对象的内部状态。

            在我看来,点语法试图让它看起来和感觉就像是直接访问状态。但实际上,它只是行为 -foo 和 -setFoo: 的语法糖。我自己,我更喜欢直言不讳。点语法有助于代码更简洁的可读性,但它无助于理解,因为没有记住你真的在调用 -foo 和 -setFoo: 可能会带来麻烦。

            在我看来,综合访问器试图使编写可以直接访问状态的对象变得容易。我的信念是,这恰恰鼓励了面向对象编程旨在避免的那种程序设计。

            总的来说,我宁愿点语法和属性从未被引入。我曾经能够告诉人们,ObjC 是对 C 的一些干净的扩展,使其更像 Smalltalk,但我认为这不再是真的了。

            【讨论】:

              【解决方案11】:

              在我看来,点语法让 Objective-C 少了 Smalltalk 式的风格。它可以使代码看起来更简单,但会增加歧义。是 structunion 还是对象?

              【讨论】:

                【解决方案12】:

                许多人似乎将“属性”与“实例变量”混为一谈。 我认为,属性的重点是可以修改对象而不必知道其内部结构。实例变量在大多数情况下是属性的“实现”(它又是“接口”),但并非总是如此:有时属性不对应于 ivar,而是计算返回值'在飞行中'。

                这就是为什么我认为“点语法会欺骗您认为您正在访问变量因此令人困惑”的想法是错误的。点或括号,你不应该对内部进行假设:它是一个属性,而不是一个 ivar。

                【讨论】:

                • 附带说明,Objective-C 对象 references 不是直接的 C 结构变量,而是更像“指向结构的指针”,因此点语法不会感觉直接访问成员;是箭头操作符-> 完成了这个角色(当然,只要说 ivars 不受访问限制)。
                【解决方案13】:

                我想我可能会切换到消息而不是点表示法,因为在我的脑海中 object.instanceVar 只是属于 objectinstanceVar,对我来说,它看起来一点也不像 方法调用,它就是这样,所以你的访问器中可能会发生一些事情,无论你使用 instanceVar 还是 self.instanceVar 可能比简单的隐式和显式有更多的区别。只是我的 2 美分。

                【讨论】:

                  【解决方案14】:

                  点表示法试图使消息看起来像是对结构成员的访问,但事实并非如此。在某些情况下可能工作正常。但是很快就会有人想出这样的东西:

                  NSView *myView = ...;
                  myView.frame.size.width = newWidth;
                  

                  看起来不错。但不是。是一样的

                  [myView frame].size.width = newWidth;
                  

                  这是行不通的。编译器接受第一个,但理所当然地不接受第二个。即使它第一次发出错误或警告,这也令人困惑。

                  【讨论】:

                    【解决方案15】:

                    叫我懒惰,但如果我必须输入一个“。”与两个 [] 每次获得相同的结果我更喜欢单个 .我讨厌冗长的语言。 lisp 中的 () 让我抓狂。数学等优雅的语言简洁而有效,其他的都达不到要求。

                    【讨论】:

                    • 问题是数学,虽然简洁有效,但不是一门常规语言,所以我们无法解析和编译它。请注意,例如,在数学函数中,通常用括号编写它们,因为没有括号会更难理解。
                    【解决方案16】:

                    使用点符号(尽可能)

                    关于返回一些值的实例方法

                    不要使用点符号

                    关于返回 void 的实例方法、init 方法或 Class 方法。

                    还有我个人最喜欢的例外

                    NSMutableArray * array = @[].mutableCopy;
                    

                    【讨论】:

                      【解决方案17】:

                      我个人在代码中根本不使用点符号。我只在需要时在 CoreData KVC 绑定表达式中使用它。

                      对我来说不在代码中使用它们的原因是点符号隐藏了 setter 语义。无论设置器语义如何(分配/保留/复制),以点表示法设置属性总是看起来像赋值。使用消息符号可以看出接收对象可以控制 setter 中发生的事情,并强调需要考虑这些影响的事实。

                      我仍在考虑在检索符合 KVC 或声明的属性的值时是否要使用点表示法,因为它确实更紧凑和可读,并且没有隐藏的语义。现在,为了保持一致性,我坚持使用消息符号。

                      【讨论】:

                      • 可能还有隐藏的事情发生。除了返回实例变量之外,getter 还可以做其他事情。即使它是已声明的属性,也不必合成 getter,或者它可以在子类中被覆盖。
                      【解决方案18】:

                      好的,Objective-C 中的点表示法看起来确实很奇怪。但是没有它我仍然无法执行以下操作:

                      int width = self.frame.size.width;
                      

                      工作正常,但是:

                      int height = [[[self frame] size] height];
                      

                      给我“无法转换为指针类型”。不过,我真的很想让我的代码看起来与消息符号一致。

                      【讨论】:

                      • -frame 返回一个 NSRect,它是一个 C 结构,而不是一个 Objective-C 对象。这就是第二个示例导致编译器错误的原因。应该是:int height = [self frame].size.height;
                      • 同意。但是括号后面的点看起来很糟糕!我通常先将矩形存储在局部变量中。
                      【解决方案19】:

                      这是一个很好的问题,我看到了很多不同的答案。虽然很多人都谈到了这些话题,但我会尝试从不同的角度来回答这个问题(有些人可能已经含蓄地回答了):

                      如果我们使用“点”符号,则方法的目标解析是在编译时完成的。如果我们使用消息传递,目标的解析将推迟到运行时执行。如果在编译时解析目标,执行速度会更快,因为在运行时解析目标包含一些开销。 (并不是说时差很重要)。由于我们已经在对象的接口中定义了属性,因此将属性的目标分辨率与运行时不同是没有意义的,因此点表示法是我们应该用于访问属性的表示法。

                      【讨论】:

                      • 您是否知道在任何情况下,ObjectiveC 中的选择器都是在运行时评估、解析和调用的?因此,点符号是旧括号样式的纯同义词。没有任何技术差异。你的速度假设是完全错误的 - 试试看,使用 Instruments 看看你自己。
                      猜你喜欢
                      • 2013-01-30
                      • 1970-01-01
                      • 2012-07-08
                      • 1970-01-01
                      • 2011-08-21
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多