objective-c编程语言 第一章 对象,类和消息 第二小节 消息(object messaging)

Objective-C编程语言

-这是一份翻译,有关于objective-c,完全出于个人学习目的,共享给大家,如需转载请注明出处
原文地址:http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html



二、消息

本节中说明了发送消息的语法,包括如何做消息嵌套。本节中同样讨论了对象实例的作用域范围,以及多态和动态绑定的概念。

1、使用消息的语法

                  要让对象做某件事情,是通过发送一条消息给对象,对象调用它的一个方法。在objc中消息是由方括号包含的,如:

                  [Receivermessage]

                  Receiver是一个对象,消息告诉它要做什么。在源代码中,消息就是由对象的方法及传递给它的参数组成的。当消息被发送后,运行时系统在Receiver的消息列表中选择合适的消息,并调用它。

                  如,下边这个消息告诉myRectagle对象执行display方法,来让矩形展示自己:

                  [myRectangledisplay];

                  消息是由分号结束的,这跟C语言是一致的。

                  对象中的方法名通常被称为选择子(selector)。方法也可以有参数,只带有一个参数的消息,在方法名的后边紧跟着一个冒号,并将参数置于冒号之后,如:

                  [myRectanglesetWidth:20.0];

                  带有多个参数的方法,其方法名和参数是交叉的,这样方法名可以很自然的描述方法所需要参数的用途。如下边这个消息告诉myRectangle对象设置其初始坐标为(30.0,50.0):

                  [myRectanglesetOriginX:30.0 Y:50];//这是一个很好的关于多个参数的例子

                  选择子的名字包含了方法名字的所有部分,包括冒号。因此前边这个例子的选择子名字就是setOrignX:Y:.它包含两个冒号,因此有两个参数。然而,选择子的名字不包含任何其它的东西,如返回值的类型或者参数的类型。

                  重要的:objc的选择子的的子部分是不可选的,它们的顺序也不能改变。在有些语言中,单词”named parameters” and “keyword parameters”暗示着参数在运行时是可变的,可以有默认的参数值,也可以按不同的顺序调用,甚至可能有可变的多余的命名参数。这些特性在objc中都不可行。Objc方法的定义跟C语言一样只能表示其有指定的参数,犹如之前的例子,只能表示其包含有限个附加的参数。

                  理论上讲Rectangle类可以如此实现setOrgin::方法,第二个参数不需要标签指示,那就可以如下调用:

                  [myRectanglesetOrgin:30.0:50.0];//这是多参数方法的一个不好的例子

                  尽管在语法上是合法的,setOrgin::并没有将参数和方法名明显的区分开来。因为第二个参数实际上未标记的,那对于代码阅读者来说他会非常难知道参数的类型是什么以及它的作用是什么。

                  在objc中可变参数的方法也是支持的,尽管它们非常少遇到,这可以通过在方法名后加上由逗号分隔的参数实现。(同冒号不一样,逗号不视为方法名的一部分)下边的例子中makeGroup:方法被传递给一个必需的参数group,其它三个参数是可选的 :

                  [receivermakeGroup:group,memberOne,memberTwo,memberThree];

                  像标准的C过程一样,方法可以有返回值。如下边的例子:

                  BOOLisFilled;

                  isFiled= [myRectangle isFilled];//变量和方法可以有相同的名字

                  一个消息也可以嵌套另一个消息表达式,如下边的这个例子,矩形的颜色并设置为另个一个矩形的颜色:

                  [myRectanglesetPrimaryColor:[otherRectangle primaryColor]];

                  Objc同样也提供一个紧凑和方便的点操作符号来调用对象的访问(accessor)方法。点操作符通常与声明的属性(declared properties)特性一起使用(请查看‘declared properties’),并在‘dotted syntax’中有详细描述。

2、向nil对象发送消息

                  1、在objc中像nil对象发送消息是允许的,实际上在运行时它什么都不做。在cocoa中有很多pattern利用了这一特性。发往nil的消息的返回值也能是有效的:

                  如果方法返回的是一个对象,那么发往nil的消息返回的是0(nil),如,

                  Person*motherInlaw = [[aPerson spouse] mother];

                  如果aPerson对象都配偶是nil,那么mother方法被发送给nil对象,返回的值也是nil。

                  2、如果方法返回的是任何类型的指针(C指针),或者是小于等于4字节的整形如,短整型(short)、整数类型(int),或者是浮点型(float),双精度(double),长双精度(long double),或者长长整型(long long)那么发往nil的消息返回的是0

                  3、如果方法返回的是一个由MAC OSX ABI中定义的结构体类型,那么此消息发往nil后返回的是一个被初始为0的结构体,结构体中的所有域都为0.对于其它的结构体,不会用0填充(即是不确定的)。

                  4、如果方法返回到是除了之前的所提到的类型,将此消息发往nil,返回值未定义。

                  下边给出了一种向nil发送消息的合法使用方法的代码段:

                  idanObjectMaybeNil = nil;

                  //这是合法的

                  If([anObjectMaybeNilmethodThatReturnsADouble] == 0.0)

                  {

                                    //方法实现

                  }

 

3、对象的实例变量

                  对象的方法自动拥有对象实例变量的访问权限,你毋须把一个对象的实例变量作为参数传递给它的方法,更不需要声明,如上边的primaryColor方法并没有参数,但它能够找到otherRectangle的颜色并且返回它。

                  这个约定可以简化objc的编程,这同样也提供了一种面向对象的思考方式,来理解对象和消息。消息被发送给对象就像是信邮递到你的家中。消息的参数从外界携带着信息传递给对象,但他们不需要将对象自己传递给对象本身。

                  一个方法只能默认的拥有接收者的实例变量,如果它需要存储在别的对象终端某个变量的信息,那必须发送一个消息到那个对象要求它显示这个变量的信息。先前的primaryColor和isFilled方法就是为了满足这种目的。

                  更多得关于实例变量的信息请查看‘defining a class’。

4、多态(polymorphism)

                  正如之前的例子说明的那样,在objc中消息的语法位置和在C语言中过程的调用是一样的。但是由于方法是属于一个对象,消息的行为就与过程调用是不一样的。

                  特别的,对象只能通过那些被它定义的方法所操作,即使是别的对象拥有同样的方法,也不能被这些方法操作。因此,两种不同的对象对相同的消息的反映可能是完全不同的。例如,每种不同的对象可能都接收到display消息,但可能通过完全不同的方式展现自己。圆和矩形可能对相同的追踪鼠标轨迹的指令反映是玩去不同的。

                  这个特性被称为多态,多态在面向对象编程中发挥着非常有意义的作用。和动态绑定(dynamic binding)一起允许你能够在不能确定具体对象的时写出的代码可能关联到任何数量的不同种类的对象,这些对象甚至可能是在以后才会被编写,并且这些对象是由其他的编程人员在其它的工程项目中编写的。如果你发送了一个display消息给一个id类型的变量,任何拥有display方法的对象都是潜在的接收者。

5、动态绑定(dynamic binding)

                  过程调用和消息的一个非常重要的不同就是过程和它的参数实在编译时就已经连接到一起了,但是消息和接收它的对象不会连接知道程序被执行,消息被发送。因此,调用哪个方法来响应某个消息只有在运行时才能够决定,而不是在编译代码时。

                  当一个消息被发送后,会有一个实时的消息映射来查找接收对象中以此消息名作为名字的方法,然后定位到此方法的实现,呼叫这个方法,并将消息接收者的实例变量的指针传递给此方法。(更多的关于这个映射的信息请查看‘messaging’在objective-c runtime programing guide)。

                  消息与对象方法的动态绑定和多态一起提供给面向对象编程许多灵活性和能力。由于每个对象也可以拥有它自己定义的版本的方法,一个objc语句因此可以产生出许多不同的结果,这不需要改变发送的消息,只需要改变接收消息的对象即可。接收对象又可以在程序运行时决定,选择哪个对象作为接收者可以通过用户输入作出选择。

                  当作执行基于AppKit的代码时,用户可以决定哪个对象来接收菜单命令,如拷贝、剪贴和黏贴,消息将流向任何任何当前选择的对象。一个显示文本的对象和一个显示图像的对象对于copy消息的响应可能是完全不同的;一个用于代表多个不同形状的对象和矩形对象对copy的响应也是不同的。消息只有在运行时才会选择具体的方法,这些方法的行为表现的不同也是互相独立的。发送消息的代码无需关心关心他们的不同实现,甚至不需要检查是否有响应这个消息的能力,因此一个应用程序的对象们会对相同的copy消息按照自己的方式作出完全不同的变现。

                  Objc在动态绑定上迈出了更远的一步,它甚至允许被发送的消息的名字可以由运行的时的变量决定。这种机制在‘messaging’在objective-c runtime programing guide有所描述。

6、动态方法解决方案

                  你可以使用动态方法的解决方案来在运行时提供类和实例方法的实现。请查看”Dynamic Method Resolution 在 Objective-C RuntimeProgramming Guide for more details”.

 

7、点语法(dot syntax)

                  Objc用点操作符提供替代方括号的方式来调用访存方法。点语法跟C语言中访问结构体元素采用相同的语法:

                  myInstance.value= 10;

printf("myInstancevalue: %d", myInstance.value);

然而当使用对象时,点语法更像是一种语法的便捷方式,在编译时它会被翻译为一个存储方法。点语法并不直接取得或设置对象的实例变量。上边的代码段与下边都是等价的:

[myInstancesetValue:10];

printf("myInstancevalue: %d", [myInstance value]);

作为一个推论,如果你想通过访存方法访问对象本身自己的实例变量,你必须显示的使用self,例如:

self.age = 10; 或者是等价的 [self setAge:10];

如果你不实用self,那你就可以直接操作的是实例变量本省,在下边这个例子中,设置实例变量age的方法就不会被调用:

Age = 10;

点语法的一个优势就是它显得更紧凑,而且比起中括号更易读,尤其是当你像在本对象中访问或修改其它对象的属性时。另一个优点是编译器可以检测到有人想改变一个只读的属性时报错,如果你用方括号语法来访问改变实例变量,最好的情况下,编译器会产生一个没有定义方法的警告,你调用了一个不存在的设置方法,这将在运行时失败。

点语法的通常用法

              当你使用点语法获取一个值时,运行时系统会调用关联的获取值的访问方法。默认的获取方法与点后变的标签采用一样的名字。用点语法设置一个值时调用设置方法,默认情况下会将点后的标签首字母大写加上set作为设置方法的名字。如果你不想使用默认的访存方法名,你也可以通过指定声明属性的特征来改变他们(请查看‘declared properties’)。

Listing 1-1说明几种用法.
Listing 1-1 通过点语法访问属性
Graphic *graphic = [[Graphic alloc] init];
NSColor *color = graphic.color;
CGFloat xLoc = graphic.xLoc;
BOOL hidden = graphic.hidden;
int textCharacterLength = graphic.text.length;
if (graphic.textHidden != YES) {
    graphic.text =@"Hello"; // @"Hello" is a constant NSString object.
}
graphic.bounds = NSMakeRect(10.0, 10.0, 20.0, 120.0);
这段代码与Listing 1-2编译后产生相同的代码
Listing 1-2 通过方括号访问属性
Graphic *graphic = [[Graphic alloc] init];
NSColor *color = [graphic color];
CGFloat xLoc = [graphic xLoc];
BOOL hidden = [graphic hidden];
int textCharacterLength = [[graphic text] length];
if ([graphic isTextHidden] != YES) {
    [graphicsetText:@"Hello"];
}

[graphic setBounds:NSMakeRect(10.0, 10.0, 20.0, 120.0)];

对于合适的C语言类型的属性,符合表达式的意义已经良好定义了,比如说你有一个NSMutableData类型的实例对象:

             NSMutableData*data = [NSMutableData dataWithLength:1024];

             你可以通过点语法和符合表达式更新实例对象的length属性:

             data.length +=1024;

data.length *= 2;

data.length /= 4;

这与下边的采用中括号的方式是等价的:

[data setLength:[data length] + 1024];

[data setLength:[data length] * 2];

[data setLength:[data length] / 4];

 

与nil值有关

              如果一个nil值在属性的遍历过程中被引入了,结果同发送消息给一个nil对象是等价的,如下边使用方法是等价的:

              //Each member of the path is an object.

x = person.address.street.name;

x = [[[person address] street] name];

// The path contains a C struct.

// This will crash if window is nil or -contentView returns nil.

y = window.contentView.bounds.origin.y;

y = [[window contentView] bounds].origin.y;

// An example of using a setter.

person.address.street.name = @"Oxford Road";

[[[person address] street] setName: @"Oxford Road"];

 

点语法的性能和线程开销

             无论你是用点语法还是中括号语法来调用访存方法,编译器都会产生等价的代码。因此这两种编码技术的性能表现是一致的,采用点语法也不会产生额外的性能依赖。

点语法的用法

              用objc的点语法作为中括号语法的一种代替方法来调用访存方法

1、  把属性作为返回值给变量赋值 aVariable = anObject.aProperty;

2、  给属性赋值 anObject.name = @"New Name";

3、  嵌套连续属性返回值 xOrigin = aView.bounds.origin.x;

4、  嵌套连续属性赋值

NSInteger i = 10;

anObject.integerProperty= anotherObject.floatProperty = ++i;

                 

点语法的不正确使用方法

             下边的使用点语法的方式是强烈不建议的,因为它们不遵守点语法的调用访存方法的意图。

1、  下边的语句会产生一个编译器警告(warning: value returned from property not used)

anObject.retain;

2、  下边的语句产生一个setFooIfYouCan:不是一个设置方法,因为它不返回void

/* Method declaration. */

- (BOOL) setFooIfYouCan: (MyClass *)newFo

/* Code fragment. */

anObject.fooIfYouCan = myInstance;

3、  下边的语句调用lockFocusIfCanDraw方法并将返回值赋值给flag,这不会产生任何编译器警告,除非是flag的类型跟返回值类型不匹配。然而,这种用法也是强烈不鼓励的,可能是lockFocusIfCanDraw是非标准的默认的属性

flag = aView.lockFocusIfCanDraw;

4、  下边产生一个编译器警告,因为readonlyProperty属性是被定义为只读的(warning: assignment to readonlyproperty 'readonlyProperty').

/*Property declaration. */

@property(readonly)NSInteger readonlyProperty;

/* Methoddeclaration. */

- (void)setReadonlyProperty: (NSInteger)newValue;

/* Codefragment. */

self.readonlyProperty = 5;

               尽管如此,由于设置readonlyProperty的方法是显示调用的,这在运行时是可以工作的,这个使用方法强烈不建议的原因是,它增加了一个不受访存权限控制的设置方法。要在声明属性时确保清楚的设置了属性的访问权限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值