Objective-C“消息” - 阅读它的正确方法是什么?

时间:2009-11-08 20:21:22

标签: objective-c object message-passing

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

我认为这很强大,但我还不确定如何使用它......

John Greets Kelly:

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

关于它的一些东西不能自然地阅读。我不确定这是一位经验丰富的Objective-c程序员是如何编写“消息”的。

有人可以解释每个参数的两个名称的原因,并且可能是一个更有用的例子,说明如何有效地使用它来在程序中加入意义吗?

同样令我感到困扰的是,第一个参数的名称与“消息”的名称基本相同。你如何通过编写有意义且易于理解的方法/'消息名称'来解决这个问题?

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
}

-(void)Greet:(char*)from toPerson:(char*)to greetWith:(char*)greeting ;

@end

@implementation Person

-(void)Greet:(char*)from toPerson:(char*)to greetWith:(char*)greeting ;
{
  printf( "%s says %s to %s\n", from, greeting, to ) ;
}

@end



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


  Person * p = [ Person alloc ] ;

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

  [ p release ] ;



  [pool drain];
  return 0;
}

5 个答案:

答案 0 :(得分:20)

第一件事:作为风格笔记,将您的大括号放在一起:

[ 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."];

请注意,我还没有将g中的greetPerson大写。这是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;
}

此代码完全未经测试,因此如果它顺利运行,我将会留下深刻的印象。

答案 1 :(得分:4)

其他人已经涵盖了最重要的一点,所以我只想谈谈一些补充问题:

  

也有些困扰我,那就是第一个参数的名称与'message'的名称基本相同。你如何通过编写有意义且易于理解的方法/'消息名称'来解决这个问题?

最常见的处理方法是将方法名称组合为“它是什么/做什么”和第一个参数的标签。例如:

 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

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

实际的参数名称(类型之后的部分)在方法定义中使用,因此实际上只有编写方法的程序员才能使用,因为那些是变量将在方法正文中提供。

答案 2 :(得分:2)

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

第二个“部分”是方法的参数,在你的例子中是问候语。

这类似于C所在的地方

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

我还应该提一下,您可能希望使用NSString *代替char *NSLog()代替printf()(不要担心,它工作几乎完全相同)。

答案 3 :(得分:0)

可能是:

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

person对象已经是John了,他正在问候另一个人对象,我不会像你那样定义为字符串,而是作为Person类的另一个实例,然后你可以做greetWithString。< / p>

[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");

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

答案 4 :(得分:0)

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

样式注释:除非您指的是首字母缩略词(如“URL”),否则不要使用大写字符启动方法名称。