RandomItems不是iOS应用,而是命令行工具。命令行工具不用开发复杂的用户界面,所以能集中精力学习Objective-C。本章和第3章的重点是学习Objective-C,第4章再开发iOS应用。
运行Xcode,选择File→New→Project…在新出现的窗口左侧选择OS X下的Application,然后选择右侧面板上方的Command Line Tool(命令行工具),如图2-3所示。单击Next按钮。
图2-3 创建一个命令行工具项目
在新出现的面板中,将项目命名为RandomItems,项目类型选择Foundation(见图2-4)。
图2-4 为项目命名
单击Next按钮,Xcode会提示保存项目。保存好项目,本书之后的项目还会用到RandomItems的部分代码。
RondomItems的第一个版本将创建一个包含4个字符串的数组。数组包含一组按序排列的对象,可以通过索引存取。其他语言可能会将类似的对象称为list(队列)或vector(向量)。数组中第一个对象的索引都是0。
创建数组后,将遍历数组打印所有字符串。Xcode控制台中的输出会类似图2-5所示。
图2-5 控制台的输出
RandomItems中有5个对象:1个NSMutableArray对象,4个NSString对象,如图2-6所示。
图2-6 NSMutableArray对象包含指向NSString对象的指针
在Objective-C中,数组所包含的“对象”并不是对象自身,而只是指向对象的指针。当程序将某个对象加入数组时,数组会保存该对象在内存中的地址。
现在请关注NSMutableArray和NSString。NSMutableArray是NSArray的子类。Objective-C中的类是以层次结构(hierarchy)的形式存在的。除了整个层级结构的根类NSObject外,每个类都有一个且只有一个父类(superclass)并继承其父类的行为。
图2-7展示了NSMutableArray和NSString的一些方法和层级结构。
图2-7 部分类的层级结构
NSObject作为位于层次结构顶部的父类,其职责是实现Cocoa Touch框架中所有对象的基本行为。所有的类都会继承NSObject中的方法和实例变量。NSObject实现了很多方法,其中两个方法是alloc和init(见图2-7)。因此所有的类都可以使用alloc和init方法创建自己的对象。
子类可以通过添加方法和实例变量扩充其继承自父类的行为:
•NSString扩充NSObject的行为,可以存储和处理字符串,也添加了很多方法,如length,可以返回一个字符串的长度。
•NSArray扩充NSObject的行为,可以按索引存取对象(objectAtIndex:),也可以获取存储的对象数量(count)。
•NSMutableArray扩充NSArray的行为,可以动态增加和删除对象。
创建数组并填充字符串
现在开始在代码中使用这些类。在项目导航面板中选择main.m文件,Xcode会在编辑区域打开该文件。读者可以看到,该文件中的部分代码是由Xcode自动生成的,其中的main函数是C(或Objective-C)程序的入口点(entry point)。
删除用NSLog打印“Hello, World!”的那行代码,添加新代码,创建NSMutableArray对象并添加4个字符串,最后释放NSMutableArray对象:
#import
int main(int argc,const char*argv)
{
@autoreleasepool{
// 在这里输入代码
NSLog(@/"Hello, World!/");
//创建一个NSMutableArray对象,并用items变量保存该对象的地址
NSMutableArray*items=[[NSMutableArray alloc]init];
//向items所指向的NSMutableArray对象发送addObject:消息
//每次传入一个字符串
[items addObject:@/"One/"];
[items addObject:@/"Two/"];
[items addObject:@/"Three/"];
//继续向同一个对象发送消息,这次是insertObject:atIndex;
[items insertObject:@/"Zero/" atIndex:0];
//释放items所指向的NSMutableArray对象
items = nil;
}
return 0;
}
加入数组的是NSString对象,可以通过在字符串前添加一个“@”前缀来创建一个NSString对象:
NSString *myString = @/"Hello, World!/";
遍历数组
现在items数组中有4个NSString对象。接下来,遍历数组中的每一个对象并将结果输出至控制台。
可以使用for循环:
for (int i = 0; i < [items count]; i++) {
NSString *item = [items objectAtIndex:i];
NSLog(@/"%@/", item);
}
因为数组的索引是从0开始的,所以计数器i的初始值为0。数组的最后一个索引是对象数量减去1,因此计数器的终值是[items count] - 1(因为计数器是整型,为方便起见写成i < [items count],而不是i <= [items count] - 1)。在循环体中,向数组发送objectAtIndex:消息,根据当前索引获取NSString对象,再输出至控制台。
数组对象所包含的对象个数是一个非常重要的信息。这是因为在获取数组对象所包含的对象时,如果使用的索引大于或等于数组对象所包含对象的个数,程序就会抛出异常(本章结尾处会介绍更多有关异常的知识)。
这段代码当然可以正常工作,但是Objective-C提供了一种更好的遍历数组的语法,称为快速枚举(fast enumeration)。快速枚举比传统的for循环简洁很多,出错概率更低,而且经过编译器的优化,通常比for循环更快。
在main.m中,添加以下代码,使用快速枚举遍历items数组:
int main (int argc, const char * argv)
{
@autoreleasepool {
//创建一个NSMutableArray对象,并用items变量保存该对象的地址
NSMutableArray *items = [[NSMutableArray alloc] init];
//向items所指向的NSMutableArray对象发送addObject:消息
//每次传入一个字符串
[items addObject:@/"One/"];
[items addObject:@/"Two/"];
[items addObject:@/"Three/"];
//继续向同一个对象发送消息,这次是insertObject:atIndex:
[items insertObject:@/"Zero/" atIndex:0];
//遍历items数组中的每一个item
for (NSString *item in items) {
//打印对象信息
NSLog(@/"%@/", item);
}
//释放items所指向的NSMutableArray对象
items=nil;
}
returnO;
}
快速枚举有一个限制:如果需要在循环体中添加或删除对象,就不能使用快速枚举,否则程序会抛出异常。这时只能设置计数器并使用普通的for循环。
构建并运行应用(Command-R),Xcode会在窗口底部显示一个新面板,称为调试区域(debug area)。显示程序输出结果的控制台位于调试区域右侧(见图2-8)。
图2-8 调试区域和控制台
如果需要改变这些面板的大小,可以拖曳调试区域和其下面板的边框。实际上,工作空间中的所有区域都可以通过拖曳边框改变大小。
读者已经完成了RandomItems的第一个版本,在开发下一个版本之前,先学习NSLog函数和格式字符串。
格式字符串
NSLog函数可以将某个指定的字符串输出至Xcode的控制台。此外,NSLog的实参个数并不确定,其中的第一个实参是必需的,且必须是NSString对象。这个实参称为格式字符串(format string)。
格式字符串可以包含文字和多个转换说明(token)。格式字符串中的转换说明(也称为格式规格)必须以百分号(%)为前缀。除了传入NSLog函数的第一个实参,每个额外传入的实参都会替换掉格式字符串中的一个转换说明。
转换说明会指定和其相对应的实参的类型。代码如下:
int a = 1;
float b = 2.5;
char c = /'A/';
NSLog(@/"Integer: %d Float: %f Char: %c/", a, b, c);
上例的控制台输出应该为:
Integer: 1 Float: 2.5 Char: A
Objective-C的格式字符串基本和C语言相同。但是Objective-C支持一种额外的转换说明:%@,对应的实参类型是指向任何一种对象的指针。
程序在处理格式字符串时,如果遇到%@,则不会将其直接替换为相应位置的实参。程序会先向相应位置的实参发送description消息,得到description方法所返回的NSString对象,然后使用得到的NSString对象替换%@。
因为程序会向%@所对应的实参发送消息,所以这些实参必须是对象。请读者回顾图2-7,可以看到NSObject实现了description方法,因此所有的Objective-C对象也都实现了该方法并可以对应%@。