【问题标题】:Objective-C "messages" - what's the right way to read it?Objective-C“消息”——阅读它的正确方法是什么?
【发布时间】:2009-11-08 20:21:21
【问题描述】:

基本上,您可以在objective-c 中声明一个方法并为每个参数命名两次

我知道这很强大,但我还不太清楚如何使用它......

当约翰迎接凯利时:

[ p Greet:"John" toPerson:"Kelly" greetWith:"hey babe" ] ;

关于它的某些内容不自然地阅读。我不确定一个有经验的 Objective-c 程序员是否会这样写“信息”。

谁能解释每个参数有两个名称的原因,并可能提供一个更有用的例子来说明如何有效地使用它来为程序赋予意义?

还有一点让我很困扰,那就是第一个参数的名称与“消息”的名称基本相同。您如何通过编写有意义且易于理解的方法/“消息名称”来解决这个问题?

#import @interface 人:NSObject { } -(void)Greet:(char*)from toPerson:(char*)to greetWith:(char*)greeting ; @结尾 @实现人 -(void)Greet:(char*)from toPerson:(char*)to greetWith:(char*)greeting ; { printf( "%s 对 %s 说 %s\n", from, greeting, to ) ; } @结尾 int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 人 * p = [ 人分配 ] ; [ p Greet:"John" toPerson:"Kelly" greetWith:"hey babe"] ; [ p Greet:"Kelly" toPerson:"John" greetWith:"get bent"] ; [p释放]; [池排水]; 返回0; }

【问题讨论】:

  • 关于编辑:我在收藏时错误地点击了downvote,然后它不会让我反转它(奇怪的错误?)。我编辑了你的答案,所以我可以交换它。​​

标签: objective-c object message-passing


【解决方案1】:

第一件事:作为风格说明,将牙套放在一起:

[ Person alloc ]

应该是

[Person alloc]

我还注意到您在分配 Person 时忘记初始化它,您应该使用:

Person *p = [[Person alloc] init];

了解如何声明方法需要一点时间。检查框架如何命名其方法很有用。对于您的具体示例,我认为您过度工程化。你正在寻找这样的东西:

Person *john = [[Person alloc] initWithName:@"John"];
Person *kelly = [[Person alloc] initWithName:@"Kelly"];

[john greetPerson:kelly withGreeting:@"Hey babe."];
[kelly greetPerson:john withGreeting:@"Get bent."];

请注意,我也没有将greetPerson 中的g 大写。这是 Objective-C 的风格惯例。

不要忘记一个对象有它自己的身份,所以你很少需要在一个对象(用来代表一个人)与某人交谈之前指示它是谁。当你写消息时,它应该读起来像英语。当你遇到多个论点时——诚然,很少见——开始思考逗号:

[john sendEmailToPerson:kelly withSubject:subject body:body attachments:nil];

看看它是如何流动的?甚至还有一些不足之处,我也没有掌握这个。给它一点时间。

Apple 的Coding Guidelines for Cocoa 是一个非常有用的文档。


另外,让自己摆脱 C 陷阱。下面是我如何编写你的整个程序(我正在介绍一大堆概念,所以不要指望理解所有内容):

#import <Foundation/Foundation.h>

@interface Person : NSObject {
    NSString *name;
}

@property (copy) NSString *name;

- (id)init;
- (id)initWithName:(NSString *)nm;
- (void)greetPerson:(Person *)who withGreeting:(NSString *)grt;

@end

@implementation Person
@synthesize name;

- (id)init {
    if (self = [super init]) {
        name = @"James Bond";          // not necessary, but default
    }                                  // values don't hurt.
    return self;
}
- (id)initWithName:(NSString *)nm {
    if (self = [self init]) {
       name = [nm copy];
    }
    return self;
}

- (void)greetPerson:(Person *)who withGreeting:(NSString *)grt {
    NSLog(@"%@ says '%@' to %@", self.name, grt, who.name);
}

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

@end

int main(int argc, const char * argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    Person *john = [[Person alloc] initWithName:@"John"];
    Person *kelly = [[Person alloc] initWithName:@"Kelly"];

    [john greetPerson:kelly withGreeting:@"StackOverflow is great, isn't it?"];
    [kelly greetPerson:john withGreeting:@"Weren't we supposed to flirt?"];

    [john release];
    [kelly release];

    [pool drain];
    return 0;
}

此代码完全未经测试,所以如果它运行顺利,我会印象深刻。

【讨论】:

  • Apple 的风格指南建议您不要在方法名称的后续部分使用“and”。 (从您刚刚链接的文档中:“不要使用“and”来链接作为接收者属性的关键字。”)所以选择器应该只是sendEmailToPerson:withSubject:body:attachments:
  • @Chuck:看到了吗?告诉你我不是专家。编辑出来。
  • 好答案。您在代码中添加的一个附加说明,但未提及:选择器的第一部分不应大写。所以你会 [john greetPerson] 而不是 [john GreetPerson:]。我知道它的吹毛求疵,但 Objective-C 程序员对语法和风格指南非常挑剔!大写的 Greet 非常Java。
  • @micmoo:谢谢,我会强调一下。
  • 很好的答案!写得很清楚。如果你想完善这个例子,你不应该使用self.name = nm,而只能使用name = nm。这与您不应在 dealloc 方法中使用 self.name = nil 的原因相同:访问器可能会产生不必要的副作用。
【解决方案2】:

其他人已经涵盖了最重要的几点,所以我将就一些补充问题进行权衡:

还有一点让我很困扰,那就是第一个参数的名称与“消息”的名称基本相同。您如何通过编写有意义且易于理解的方法/“消息名称”来解决这个问题?

最常见的处理方法是让方法名称由“它是什么/做什么”和第一个参数的标签组合而成。示例:

 NSColor * color = [NSColor colorWithDeviceRed:0.5 green:0.5 blue:0.5 alpha:0.5];

谁能解释每个参数有两个名称的原因,并可能提供一个更有用的例子来说明如何有效地使用它来赋予程序含义?

快速的回答是,这两个名字是针对不同的受众的;一个是给方法的使用者的,另一个是给方法的作者的。再次考虑上面的例子。方法声明如下所示:

+ (NSColor *)colorWithDeviceRed:(CGFloat)red
                          green:(CGFloat)green
                           blue:(CGFloat)blue
                          alpha:(CGFloat)alpha

当用户调用该方法时,他们关心的是第一个标签(冒号之前的标签)。您可以在我的第一个示例中看到,其中通道值作为数字常量传递,在代码中只能看到标签。

实际的参数名称(类型后面的部分)在方法定义中使用,因此实际上只有编写方法的程序员才能使用,因为这些变量是将在方法体本身中可用。

【讨论】:

    【解决方案3】:

    方法的第一个“部分”是selector,对于您的示例,这包括-greet:toPerson:greetWith:,这是方法的实际名称。

    第二个“部分”是方法的参数,在您的示例中,它们是问候语。

    这类似于 C 之类的东西

    int greet(string to, string greeting) {
        print(greeting, to);
    }
    

    我还应该提到,您可能会想要使用NSString * 而不是char *NSLog() 而不是printf()(不用担心,它的工作原理几乎完全相同)。

    【讨论】:

      【解决方案4】:

      大概是这样的:

      [personJohn greetPerson:@"Kelly" withGreeting:@"hey babe"];
      

      person 对象已经是 John,他正在问候另一个 person 对象,我不会像您那样将其定义为字符串,而是定义为 Person 类的另一个实例,然后您可以执行 greetWithString

      [personJohn greetPerson:personKelly withGreeting:@"hey babe"];
      

      您没有定义两次参数,这就是您感到困惑的地方。如果你懂 C++,你通常会:

      void greetPerson(Person thePerson, string greeting);
      

      以函数原型为例。然后你会这样称呼它:

      greetPerson(personKelly, "hey babe");
      

      现在,objective-c 所做的一切都是通过自我记录让您轻松自如。不是简单地将参数放入函数中,而是在声明它们之前命名它们,因此上面的调用将是:

      greetPerson(Person:personKelly, greeting:"hey babe");
      

      鉴于上面发布的函数原型。这样,当您阅读代码时,您就知道每个参数在做什么,因此可以自我记录。一开始可能看起来很乏味,但代码的可读性大大提高了。

      【讨论】:

        【解决方案5】:

        您上面描述的方法的名称是“Greet:toPerson:greetWith:”。冒号是名称的一部分。参数(及其类型说明符)不是。

        样式说明:方法名称不要以大写字符开头,除非您指的是首字母缩略词(如“URL”)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-30
          • 1970-01-01
          • 2011-12-13
          • 2015-01-20
          相关资源
          最近更新 更多