关于对象:Objective-C“消息” – 阅读它的正确方法是什么?

Objective-C “messages” - what's the right way to read it?

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

我知道这很强大,但我还不太确定如何使用它…

当约翰问候凯利时:

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

有些东西读起来不自然。我不确定一个有经验的Objective-C程序员是否会这样写"消息"。

有人能解释每个参数有两个名称的原因吗?可能还有一个更有用的例子,说明如何有效地在程序中赋予意义?

还有一些事情困扰着我,那就是第一个参数的名称基本上与"消息"的名称相同。如何在编写有意义和可理解的方法/"消息名称"时解决这一问题?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#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
"
, 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;
}


第一件事:作为一个风格说明,把你的牙套放在一起:

1
[ Person alloc ]

应该是

1
[Person alloc]

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

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

了解如何声明方法需要一点时间。检查框架如何命名其方法是有用的。对于你的具体例子,我认为你设计得太高了。你在找这样的东西:

1
2
3
4
5
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大写。这是客观C的风格惯例。

别忘了一个对象有它自己的身份,所以在和某人交谈之前,你很少需要指导一个对象(意味着代表一个人)它是谁。当你写一条信息时,它应该像英语一样读。当你陷入多个争论中时——诚然,很少——开始用逗号思考:

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

看到了吗?即使这样,也有一些是需要的,我还没有掌握这一点。给它时间。

关于这方面的一个非常有用的文档是苹果公司的可可编码指南。

另外,把你自己从C陷阱里弄出来。以下是我如何编写您的整个程序(我将介绍大量的概念,因此不要期望理解所有这些概念):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#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;
}

这段代码完全没有经过测试,所以如果它能毫无障碍地工作,我会得到很好的印象。


其他人已经讨论了最重要的几点,所以我将讨论一些补充问题:

Also something bothers me and that is the name of the first parameter is basically the same as the name of the 'message'. How do you resolve that in writing meaningful and understandable method/'message names'?

处理此问题的最常见方法是使方法名由"它是什么/做什么"和第一个参数的标签组合而成。例子:

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

Can someone explain the reason for two names for each parameter and possibly a more useful example of how this can be used effectively to put meaning in the program?

快速的答案是,这两个名称适用于不同的访问群体;一个用于方法的用户,另一个用于方法的作者。再考虑一下上面的例子。方法声明如下所示:

1
2
3
4
+ (NSColor *)colorWithDeviceRed:(CGFloat)red
                          green:(CGFloat)green
                           blue:(CGFloat)blue
                          alpha:(CGFloat)alpha

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

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


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

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

这类似于C这样的东西

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

我还应该提到,你可能希望与NSString *合作,而不是与char *合作,与NSLog()合作,而不是与printf()合作(不用担心,工作原理几乎完全相同)。


您在上面描述的方法的名称是"greet:toperson:greetwith:"。冒号是名称的一部分。参数(及其类型说明符)不是。

样式说明:不要以大写字符开头方法名,除非您引用的是首字母缩略词(如"url")。


可能是:

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

这个人对象已经是约翰,他正在问候另一个人对象,我不会像你那样把它定义为一个字符串,而是作为类人的另一个实例,然后你可以做greetWithString

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

你没有定义两次参数,这就是你困惑的地方。如果你知道C++,你通常有:

1
void greetPerson(Person thePerson, string greeting);

例如,作为函数原型。那么你可以这样称呼它:

1
greetPerson(personKelly,"hey babe");

现在,所有的目标C都是通过自我记录让你变得容易。与其简单地将参数放在函数上,不如在声明它们之前对它们进行命名,因此上面的调用是:

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

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