要使用某个类的对象,必须先得到一个指向该对象的变量(variable)。这类“指针变量”保存的是对象在内存中的地址,而不是对象自身(所以是“指向”某个对象)。下面为一个“指向对象的变量”的声明示例:
Party*partyInstance;
这段声明代码只创建了一个指针,并没有创建任何Party对象,仅仅是声明了一个可以指向某个Party对象的指针变量,变量名是partyInstance。请注意变量名之前没有加下画线,所以它不是实例变量。
创建对象
对象是有生命周期的:首先被创建出来,然后接收消息,最后在不需要时被释放。
向某个类发送alloc消息,可以创建该类的对象。类在收到alloc消息后,会在内存中创建对象(在堆上创建,和调用malloc函数的效果相同),并返回指向新对象的指针,这时程序就可以将这个指针保存在某个变量中:
Party*partyInstace=[Party alloc];
上面这行代码创建了一个指向Party对象的指针。程序得到指向某个对象的指针后,就可以向该对象发送消息。对新创建的对象,必须先向其发送一个初始化消息(initialization message)。虽然向类发送alloc消息能够创建对象,但是在完成初始化之前,新创建的对象还无法正常工作。
Party *partyInstance = [Party alloc];
[partyInstance init];
因为任何一个对象都必须在创建并且初始化后才能使用,所以上述两个消息应该写在一行代码里,其代码如下:
Party *partyInstance = [[Party alloc] init];
这种将两个消息合写在一行代码中的做法称为嵌套消息发送(nested message send)。程序会先执行最里面那个方括号中的代码,所以Party类会先收到alloc消息。接着,alloc方法会返回指向新创建对象的指针。最后,未初始化的对象会收到init消息,返回初始化后的对象指针,并将指针保存在变量中。
发送消息
一旦某个对象完成了初始化,就可以向其发送更多其他的消息。
下面进一步介绍消息发送语法的组成结构。首先,消息必须写在一对方括号中。方括号中的消息包含如下三个部分。
接收方(receiver)指针,指向执行方法的对象选择器(selector)需要执行方法的方法名实参(arguments)以变量形式传给方法的数值
以Party类为例,向Party对象发送addAttendee:消息,可以添加参加聚会的客人:
[partyInstance addAttendee:somePerson];
向partyInstance(接收方)发送addAttendee:消息会触发addAttendee:方法(取决于选择器),并传入somePerson(实参)。
addAttendee消息只有一个参数,但是Objective-C语言中的方法可以有很多实参,或者没有实参,例如init消息就没有实参。
收到邀请的客人要回复是否参加,并告诉主人会带来的食物。因此,Party对象还要一个名为addAttendee:withDish:的方法。这个方法有两个实参:“客人”和他准备带来的“食物”。每个实参都会和选择器中的相应标签配对,每个标签都会以冒号结尾。所有的标签合在一起构成选择器(见图2-2)。
图2-2 消息发送代码的各个组成部分
标签和参数必须配对的语法是Objective-C的一项重要特性。在其他语言中,上面这行代码可能会写成:
partyInstance.addAttendeeWithDish(somePerson, deviledEggs);
在这些语言中,传入函数的各个数值分别对应哪个参数并不明显。在Objective-C中,每个数值都会和相应的标签配对,代码如下:
[partyInstance addAttendee:somePerson withDish:deviledEggs];
读者可能要花些时间来习惯这种语法,一旦熟悉后,就会发现这种在选择器中插入实参的语法能更容易读懂代码。这里要记住,每一组方括号只对应一条需要发送的消息。虽然这里的addAttendee:withDish:有两个标签,但仍只是一条消息,发送这条消息只会触发一个方法。
在Objective-C中,方法的唯一性取决于方法名。因此,即使参数类型或返回类型不同,一个类也不能有两个名称相同的方法。但是不同的方法可包含某个相同的标签,前提是这些方法的名称并不完全相同。以Party类为例,它有addAttendee:和addAttendee:withDish:两个方法。这是两个不同的方法,不共享任何代码。
此外,还要注意消息和方法之间的区别:方法是指一块可以执行的代码,而消息是指要求类或对象执行某个方法的动作。此外,消息的名称和将要执行的方法的名称一定是相同的。
释放对象
将指向对象的变量设置为nil,可以要求程序释放该对象,代码如下:
partyInstance = nil;
这行代码会释放partyInstance变量所指向的对象(实际情况会更复杂,第3章会详细介绍内存管理方面的知识)。
nil是值为0的指针(对应C语言中的NULL,Java语言中的null)。一个值为nil的指针通常代表其没有指向任何对象。仍以Party对象为例,举办聚会需要场地(venue)。当主办方正在决定应该在何处举办聚会时,代表场地的实例变量venue的值将是nil。通过判断指针变量的值是否为nil,可以实现相应的逻辑处理,代码如下:
if (venue == nil) {
[organizer remindToFindVenueForParty];
}
Objective-C程序员通常会使用更短小精炼的语法来判断某个指针是否为nil,代码如下:
if (!venue) {
[organizer remindToFindVenueForParty];
}
因为!运算符的意思是“不”,所以if(!venue)的意思是“如果venue的值为空”,或者如果venue的值是nil,那么这条表达式的运算结果将为真(true)。
Objective-C允许向某个值为nil的变量发送消息,且不会发生任何事情。在其他语言中,向空指针(值为0的指针)发送消息是非法的,因此,通常要先检查指针是否为空,代码如下:
//是否为nil?
if(venue){
[venue sendConfirmation];
}
在Objective-C中,因为程序会忽略发送给nil的消息,所以无需做这样的检查,直接发送消息即可,代码如下:
[venue sendConfirmation];
如果venue的值是nil,那么向其发送sendConfirmation消息不会有任何结果(反之,如果某个应用应该完成某项功能,但实际没做任何事情,那么问题很可能出在某个指针变量上——原本应该指向某个对象的指针,实际的值却是nil)。
理论知识就学习到这里,现在请读者跟着本章完成一个新项目——RandomItems。