2.3 常用类介绍

了解Objective-C的一些基本知识后,本节将介绍iOS应用开发中常遇到的一些类。这些类都来自于Foundation框架,Foundation框架拥有100多个类,我们介绍的类主要集中在iOS应用开发中经常用到的几个类,分别是NSString类、NSArray类及NSDictionary类。

2.3.1 NSString类

每一种面向对象的开发语言都有自己处理字符串的类,NSString 类就是 Objective-C语言中用来处理字符串的类。在之前的例子里大家已经见过 NSString 类的一些基本运用了,其标志是双引号内的字符串前的@,如@"Hello"。

NSString类的初始化有很多种方法,可以直接赋值创建常量字符串:

        1: NSString *aString = @"This is a String";

可以通过类的一般初始化方法来初始化NSString对象之后再赋值:

        1: NSString *aString = [[NSString alloc] init];
        2: aString = @"This is a String";

还可以通过initWithString:方法把上面的两行代码并到一行来快速初始化:

        1: NSString *aString = [[NSString alloc] initWithString: @"This is a String"];

也可以用类方法stringWithFormat:通过格式字符串和参数来创建NSString:

        1: NSString *aString = [NSString stringWithFormat: @"你的年龄是:&d, 你来自 %@", 26,
    @"中国"];

类方法这个概念希望大家还记得,当定义一个方法时可以使用减号(-),也可以使用加号(+)来修饰方法,以加号来修饰的方法称为类方法,类方法可以直接通过类名来调用,而不需要先实例化对象后通过对象来调用,而 stringWithFormat:正是这样的方法。该方法的定义如下:

        + (id) stringWithFormat: (NSString *) format, …;

NSString类还有一些其他的用法,例如可以用initWithContentsOfFile:方法通过文本文件里的内容来初始化字符串:

        1: NSString *path = @"path.txt";
        2: NSString *aString = [[NSString alloc] initWithContentsOfFile: path];

除了这些丰富的初始化方法外,NSString还有很多有用的字符串处理方法,可以使用length 方法来返回字符串中的字符个数。length 方法的有用之处还在于它能够很好地处理国际字符串,不管使用的是中文、英语、日语还是法语,length 方法能返回准确的相应语言的文字个数。

        1: NSLog(@"aString字符串的长度是:%d。", [aString length]);

NSString 类还支持字符串的比较,isEqualToString:方法可以用来比较对象和方法参数接受的字符串内容是否相同。isEqualToString:返回BOOL型数据表示两个字符串是否相同。isEqualToString:的具体使用例子如下:

        1: NSString *str1 = @"String 1";
        2: NSString *str2 = [NSString stringWithFormat: @"String &d", 1];
        3: if ([str1 isEqualToString: str2])
        4:   NSLog (@"两个字符串相同");

更多信息:在Objective-C中,BOOL值是YES或NO,而不是我们在C#或Java语言中使用的TRUE或FALSE。

要比较两个字符串,还可以使用compare方法。方法声明如下:

        -    (NSCompparisonResult) compare: (NSString *) string;

compare 方法将接收对象和传递来的字符串逐个字符地进行比较,它返回一个NSComparisonResult的枚举类型来显示比较结果:

        typedef enum _NSComparisonResult{
            NSOrderedAscending = -1;
            NSOrderedSame,
            NSOrderedDescending
        } NSComparisonResult;

下面是使用compare:方法的例子:

        1:[@"abcde" compare: @"abcdg"]//返回NSOrderedDescending;

如果希望不分大小写比较字符串,可以使用另一个带options参数的compare方法:

        -(NSCompparisonResult) compare: (NSString *) string
                              options: (unsigned) mask;

这里的mask是一个掩码,可以单独使用以下三个选项,也可以通过位或运算符(|)来组合使用以下三个选项。

➢ NSCaseInsensitiveSearchL:不区分大小写字符。➢ NSLiteralSearchL:进行完全比较,区分大小写;

➢ NSNumbericSearchL:比较字符串的字符个数,而不是字符值。

最后还有几个NSString方法用来判断字符串是否包含其他的字符串。下面的两个方法中,hasPrefix:检查字符串是否以另一个字符串开头,hasSuffix:检查字符串是否以另一个字符串结尾:

        - (BOOL) hasPrefix: (NSString *) aString;
        - (BOOL) hasSuffix: (NSString *) aString;

如果希望知道子字符串在字符串中的情况,可以使用rangeOfString:方法:

        - (NSRange) rangeOfString: (NSString *) aString;

更多信息:NSRange(范围)是一个结构体,定义如下:

        typedef struct _NSRange{
                unsigned int location;
                unsigned int length;
        } NSRange;

其中location字段存放该范围的起始位置,而length则存放该范围所含元素的个数,例如“Hello, Welcome to Objective-C world!”,其中“Objective-C”可以用location为18, length 为 11 的范围来表示。可以调用 NSMakeRange 来返回一个 NSRange 结构体, NSMakeRange的定义如下:

        NSRange NSMakeRange(
                unsigned int location,
                unsigned int length
        );

下面的代码返回的range变量range.location的值是10,而range.length的值为6,如果传递的参数在接收字符串中没有找到,那么range.location的值为NSNotFound。

        1:NSRange range = [@"This is a string" rangeOfString: @"string"];

最后需要注意的是,NSString类是不可变的,NSString对象一旦被创建,可以用来生成新的字符串,查找字符或者将它与其他字符串比较,但是不能够以删除字符或添加字符的方式来修改该字符串。如果需要修改字符串,需要使用NSString的子类NSMutableString, NSString和NSMutableString的关系就好像Java或C#中的String和StringBuffer的区别。

可以使用stringWithCapacity:类方法来创建一个新的NSMutableString对象,这个方法接收一个 unsigned int 的 capacity 参数,用来指定 NSMutableString 对象的初始容量, NSMutableString将预分配一块内存来存储它,例如:

        1:NSMutableString *aString = [NSMutableString stringWithCapacity: 20];

注意capacity参数指定为20并不意味着aString只能够接受20个字符,20个字符只是其初始容量,继续增加字符是可以的,但是需要向 Objective-C 运行时环境请求额外的内存分配而会影响到性能,因此建议根据实际情况,尽量在初始化时指定足够的长度。

还可以直接初始化NSMutableString:

        1:NSMutableString *aString = [[NSMutableString alloc] initWithString: @"Hello
    World!"];

在对象初始化后可以对可变字符串进行各种操作,比如使用 appendString:或appendFormat:在字符串后面附加新字符串:

        - (void) appendString: (NSString *) aString;
        - (void) appendFormat: (NSString *) format, …;

还可以使用deleteCharactersInRange:方法删除字符串中指定位置、长度的字符:

        - (void) deleteCharactersInRange: (NSRange ) range;

还可以使用replaceCharactersInRange: withString:方法,用其他字符串替换指定位置的字符串,其中被替换部分的起始位置和被替换部分的长度由NSRange变量来指定:

        - (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString;

关于NSString及NSMutableString的更多初始化和用法可以查看XCode相关帮助文档,如果想了解类的详细定义,按住键,单击类名可以查看方法的详细定义,如图2-22所示。

图2-22 查看NSString类的定义

图2-22 显示的是 NSString 的一些方法接口定义,注意 NSString 接口后面的NSStringExtensionMethods,它是对 NSString 类方法的扩展,称为“类别”。“类别”是许多面向对象编程语言都在添加的一个新特性,能够帮助开发者扩展既有类的方法,在后续章节中会有详细介绍。

如果需要查看类或方法的概要信息,按住Option键,单击类名或方法名可以查看类或方法的基本信息,比如定义,可用性等,如图2-23所示。

图2-23 查看NSString类的概要说明

2.3.2 NSArray类

每种面向对象的编程语言都会有自己的数组方面的处理类,Objective-C也不例外,其中最常用的一种就是NSArray。NSArray类用来存储对象的有序列表,可以在NSArray中放入任意类型的对象。但需要注意的是,NSArray只能存储Objective-C的对象,不能直接存储基本数据类型如int、float等。本节将首先介绍NSArray的通常用法,然后介绍从Xcode 4.5开始,有哪些变化让我们能更方便地使用NSArray对象。

可以通过初始化方法initWithObjects:方法来创建新的NSArray对象:

        1: NSArray *array = [[NSArray alloc] initWithObjects: @"北京", @"上海", @"广州", @"
    深圳", nil];

initWithObjects:方法可以接收多个对象作为初始化的参数,最后在列表结尾添加 nil表示告诉NSArray列表到这里就结束。

还可以使用类方法arrayWithObjects:创建新的NSArray对象:

        1: NSArray *array = [NSArray arrayWithObjects: @"北京", @"上海", @"广州", @"深圳", nil];

由于NSArray只能存储对象而不能存储基本类型,因此如果希望在一个NSArray对象中存储基本类型,需要首先将基本类型转换为对象类型。下面的例子中通过NSNumber类的numberWithDouble:方法将浮点数转换为对象存储在NSArray对象中。

        1: NSArray *array = [NSArray arrayWithObjects:
        2:        [NSNumber numberWithDouble:6.578],
        3:        [NSNumber numberWithDouble:7.588],
        4:        [NSNumber numberWithDouble:8.635],
        5:        nil];

可以通过count方法来获得NSArray数组所包含的对象数目,还可以通过下面的方法来获得特定索引处的对象:

        - (id) objectAtIndex: (unsigned int) index;

与NSString类一样,NSArray类是不可变数组,一旦创建了NSArray对象,就再也不能往对象中添加或删除任何元素了。当然数组中包含的对象本身是可以修改的,如果NSArray中存储的是Person类对象,那么还是可以自由地修改Person类对象的姓名、年龄等成员变量值的。NSArray类也存在着对应的可变数组NSMutableArray类,可以用类方法arrayWithCapacity:通过指定初始容量对NSArray对象进行初始化:

        1:NSMutableArray *array = [NSMutableArray arrayWithCapacity: 20];

注意上面的代码例子中指定的 20 也只是表示初始分配的内存容量,后续增加容量是完全可以的。也可以直接初始化NSMutableArray对象:

        1:NSMutableArray *array = [NSMutableArray arrayWithObjects: @"上海", @"北京", @"广
    州", nil];

由于是可变数组,对象初始化以后,可以通过addObject:在数组末尾添加对象:

        - (void)addObject:(id)anObject;

还可以使用 removeObjectAtIndex:来删除指定位置的对象。和任何其他的语言一样, Objective-C的索引也是从0开始计数的:

        - (void)removeObjectAtIndex:(unsigned) index;

如果需要访问数组的元素,可以通过[array index]来获取指定位置的对象,也可以通过NSEnumerator对象对数组进行枚举。枚举的概念是对数组中的每一个元素逐一读取:

        1:NSMutableArray *array = [NSMutableArray arrayWithObjects: @"北京", @"上海", @"广
    州", nil];
        2: NSEnumerator *enumerator = [array objectEnumerator];
        3: id nextObject;
        4: while(nextObject = [enumerator nextObject])
        5:{
        6:   NSLog("Object: %@", nextObject);
        7:}

NSMutableArray还支持快速枚举,可以通过类似于C#里面的foreach循环语法来读取NSArray中的每一个对象:

        1:NSMutableArray *array = [NSMutableArray arrayWithObjects: @"a", @"b", @"c", nil];
        2: for(NSString *aString in array)
        3:{
        3:   NSLog("String: %@", aString);
        4:}

了解了NSString和NSArray两个类的使用方式后,看一个结合的例子。在实际编程中,经常会遇到要将一个有规律的字符串分割开来存储的例子,例如需要把“a,b,c”这个字符串以“,”分割成三个字符。这种情况可以使用 NSString 的 componentsSeparatedByString:来分割字符串并将结果存储在NSArray对象中:

        1:NSString *aString = [[NSString alloc] initWithString:@"a,b,c"];
        2: NSArray *array = [aString componentsSeparatedByString: @","];

也可以进行相反的操作,通过NSArray对象的componentsJoinedByString:方法,将数组中的元素通过指定的连接符合并到一个字符串里去:

        1: NSArray *array = [[NSArray alloc] initWithObjects: @"a", @"b", @"c", nil];
        2: NSString *aString = [array componentsJoinedByString: @","];

从Xcode 4.5开始,定义NSArray有更简单的方法,可以通过下面的代码来直接定义一个NSString类型的字符串数组:

        1:NSArray *array = @[
        2:   @"北京",
        3:   @"上海",
        4:   @"广州"];

所有在@[与]之间的内容都会被当做对象存储在数组中,而且不需要在列表的最后添加nil来告诉NSArray列表结束。nil对于arrayWithObjects:方法来说是必要的,但对于上面这种新Xcode 4.5以来的新初始化方法,不再需要在数组的最后添加nil来表示数组结束。

此外,对于基本数据类型的处理更加容易,在Xcode 4.5以前要首先将基本数据类型转换为对象才能进行处理,但从Xcode 4.5开始,这块也得到了简化,首先就体现在初始化NSNumber对象更加容易了,以前需要类似于如下的代码来初始化:

        1:NSNumber *number = [NSNumber numberWithDouble:6.588];

现在可以直接给NSNumber对象赋值来初始化:

        1:NSNumber *number = @6.588;

NSArray数组对象的初始化也变得更简单:

        1: NSArray *array = @[
        2:        @6.578,
        3:        @7.588,
        4:        @8.635];

另外一处改善在于需要获取数组内的某个特定位置的内容时,不再需要通过objectAtIndex:方法来获取,可以直接通过[索引值]的方式来获得指定位置的对象,例如针对上面的浮点数数组,下面的方法将输出7.588:

        1: NSLog(@"%@", array[1]);

2.3.3 NSDictionary类

NSDictionary 类是另外一个经常用来存储数据的类,NSArray 类的存储机制是顺序存储对象,而NSDictionary类则是以键/值对的方式存储和管理数据的,比如存储一组Person类对象就可以以每个人的姓名作为键,以 Person 类对象本身作为值存储在 NSDictionary对象中,之后只要知道某个人的姓名,就可以把相应的Person对象取出来(当然,考虑到重名的问题,姓名也许不是一个好的键,可以考虑以组织中个人的员工号或者身份证号等来唯一标识每一个对象)。

可以通过类方法initWithObjectsAndKeys:来初始化NSDictionary对象:

        1:NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:@"漩涡
    鸣人", @"姓名", @"10月10日", @"出生日期", @"火之国", @"国籍",nil];

虽然我们常说键/值对,但从方法名就可以看到该方法实际上是值/键对,赋值时该方法以值和键相互交替的方式从参数列表中读取数据,比如第1组键/值对中“姓名”是键,“旋涡鸣人”是值,后续根据需要可以添加更多的键/值对,参数列表以nil作为终止符号。可以通过objectForKey:方法来获取NSDictionary对象中的值:

          1:NSString *aString = [dictionary objectForKey: @"国籍"];

上面的代码调用将会返回值“a”,objectForKey返回值是id类型,这里可以直接赋给NSString类型是因为Objective-C运行时环境能够智能地识别到存储对象的原始类型。

NSDictionary类也可以通过类方法dictionaryWithObjectsAndKeys:来进行初始化,例如前面的代码也可以这样写:

          1:NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"漩涡鸣人
      ", @"姓名", @"10月10日", @"出生日期", @"火之国", @"国籍",nil];

同样,NSDictionary类属于不可变类,不能在创建后再动态地添加、删除存储在其中的对象,如果需要获得可变的对象,可以使用NSDictionary类的子类NSMutableDictionary。可以通过类方法dictionaryWithCapacity:来创建NSMutableDictionary对象:

          1:NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:
      20];

与前面的例子一样,这里的初始化容量也只是一个建议值,而不是对其容量大小的限制。还可以使用类方法dictionary来创建对象:

          1:NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

可以使用setObject:forKey:方法为字典添加元素:

          1:[dictionary setObject: @"姓名" forKey: @"旋涡鸣人"];
          2: [dictionary setObject: @"出生日期" forKey: @"10月10日"];
          3: [dictionary setObject: @"国籍" forKey: @"火之国"];

如果对字典中已经存在的关键字使用 setObject:forKey:方法,那么这个方法将使用新值替换原有数值。如果想在可变字典中删除一个关键字,则可使用removeObjectForKey:方法:

          1:[dictionary removeObjectForKey: @"1"];

Xcode4.5开始对NSDictionary对象也有相应的优化:

          1:NSDictionary *dictionary = @{
          2:        @"姓名": @"漩涡鸣人",
          3:        @"出生日期": @"10月10日",
          4:        @"国籍": @"火之国"};

该方法与NSArray的初始化方法非常类似,但是NSArray的[换成了NSDictionary的{符号,如果要获取NSDictionary对象中的值,从Xcode 4.5开始也可以通过以下的简便方式:

          1:NSLog(@"%@", dictionary[@"国籍"]);