15.2.2 可变对象与不可变对象

通过编写如下语句:


@“Programming is fun”


创建字符串对象时,就创建了一个内容不可更改的对象。这称作不可变对象。NSString类处理不可变字符串。你经常需要处理字符串并更改字符串中的字符。例如,可能想从字符串中删除一些字符或对字符串执行搜索替换操作。这些类型的字符串是使用NSMutableString类处理的。

代码清单15-3显示在程序中处理不可变字符串的基本方式。

代码清单15-3


//Basic String Operations

import<Foundation/NSObject.h>

import<Foundation/NSString.h>

import<Foundation/NSAutoreleasePool.h>

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

NSString*str1=@“This is string A”;

NSString*str2=@“This is string B”;

NSString*res;

NSComparisonResult compareResult;

//Count the number of characters

NSLog(@“Length of str1:%lu”,[str1 length]);

//Copy one string to another

res=[NSString stringWithString:str1];

NSLog(@“copy:%@”,res);

//Copy one string to the end of another

str2=[str1 stringByAppendingString:str2];

NSLog(@“Concatentation:%@”,str2);

//Test if 2 strings are equal

if([str1 isEqualToString:res]==YES)

NSLog(@“str1==res”);

else

NSLog(@“str1!=res”);

//Test if one string is<,==or>than another

compareResult=[str1 compare:str2];

if(compareResult==NSOrderedAscending)

NSLog(@“str1<str2”);

else if(compareResult==NSOrderedSame)

NSLog(@“str1==str2”);

else//NSOrderedDescending

NSLog(@“str1>str2”);

//Convert a string to uppercase

res=[str1 uppercaseString];

NSLog(@“Uppercase conversion:%s”,[res UTF8String]);

//Convert a string to lowercase

res=[str1 lowercaseString];

NSLog(@“Lowercase conversion:%@”,res);

NSLog(@“Original string:%@”,str1);

[pool drain];

return 0;

}


代码清单15-3输出


Length of str1:16

Copy:This is string A

Concatentation:This is string AThis is string B

str1==res

str1<str2

Uppercase conversion:THIS IS STRING A

Lowercase conversion:this is string a

Original string:This is string A


代码清单15-3首先定义了3个不可变的NSString对象:str1、str2和res。前两个初始化为常字符串对象。声明


NSComparisonResult compareResult;


声明了compareResult保存该程序后面将要执行的字符串比较操作的结果。

Length方法可以用来对字符串中的字符进行计数。输出验证了字符串


@“This is string A”


包含16个字符。

语句


res=[NSString stringWithString:str1];


展示了如何使用另一个字符串的内容来生成一个新字符串。结果NSString对象被赋值给res,然后显示以验证结果。实际的字符串内容复制是在这里进行的,而不是对内存中的同一字符串的引用。即str1和res指向两个不同的字符串对象,这与简单地执行如下赋值操作是不同的:


res=str1;


这仅仅创建了内存中同一对象的另一个引用。

stringByAppendingString:方法可以用来连接两个字符串。所以,表达式


[str1 stringByAppendingString:str2]


创建了一个新对象,这个对象由str1之后是str2的字符组成,返回结果。这项操作没有改变原字符串对象str1和str2(它们不能更改,这是因为它们都是不可变字符串对象)。

然后使用isEqualToString:方法来检测两个字符串是否相等,即,是否包含相同的字符。如果需要确定两个字符串的顺序,例如,如果要对字符串数组进行排序。可以使用compare:方法来代替。与前面用来比较两个NSNumber对象的compare:方法相似:如果从词汇上说第一个字符串小于第二个字符串,则结果是NSOrderedAscending;如果两个相等,则结果是NSOrdered-Same;如果第一个字符串大于第二个,则结果是NSOrderedDescending。如果不想执行大小写敏感的比较,则使用caseInsensitiveCompare:方法而不是compare:方法。在这个例子中,使用caseInsensitiveCompare:比较两个字符串对象@“Gregory”和@“gregory”,结果是相等。

uppercaseString和lowercaseString方法是代码清单15-3中使用的最后两个NSString方法,它们分别将字符串转换成大写字符和小写字符。再次提醒一下,该操作没有改变原字符串,最后一行输出可以证明。

代码清单15-4举例说明额外的字符串处理方法。这些方法允许你提取字符串中的子字符串,并且能够在一个字符串中搜索另一个字符串。

一些方法需要指定范围来确定子字符串。范围包括开始索引数和字符计数,索引数以0开始,因此使用数字对{0,3}指定字符串中前3个字符。NSString类(和其他的Foundation类)中的一些方法使用了特殊的数据类型NSRange来创建范围指定。它定义在<Foundation/NSRange.h>(<Foundation/NSString.h>中已经包括这个头文件)中,实际上,它是结构的typedef定义,该结构包含location和length两个成员。代码清单15-4中使用了这个数据类型。

注意第13章讲述了结构。然而,你能够从本章之后的讨论中学到足够信息来处理它们。

代码清单15-4


//Basic String Operations-Continued

import<Foundation/NSObject.h>

import<Foundation/NSString.h>

import<Foundation/NSAutoreleasePool.h>

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

NSString*str1=@“This is string A”;

NSString*str2=@“This is string B”;

NSString*res;

NSRange subRange;

//Extract first 3 chars from string

res=[str1 substringToIndex:3];

NSLog(@“First 3 chars of str1:%@”,res);

//Extract chars to end of string starting at index 5

res=[str1 substringFromIndex:5];

NSLog(@“Chars from index 5 of str1:%@”,res);

//Extract chars from index 8 through 13(6 chars)

res=[[str1 substringFromIndex:8]substringToIndex:6];

NSLog(@“Chars from index 8 through 13:%@”,res);

//An easier way to do the same thing

res=[str1 substringWithRange:NSMakeRange(8,6)];

NSLog(@“Chars from index 8 through 13:%@”,res);

//Locate one string inside another

subRange=[str1 rangeOfString:@“string A”];

NSLog(@“String is at index%lu, length is%lu”,

subRange.location, subRange.length);

subRange=[str1 rangeOfString:@“string B”];

if(subRange.location==NSNotFound)

NSLog(@“String not found”);

else

NSLog(@“String is at index%lu, length is%lu”,

subRange.location, subRange.length);

[pool drain];

return 0;

}


代码清单15-4输出


First 3 chars of str1:Thi

Chars from index 5 of str1:is string A

Chars from index 8 through 13:string

Chars from index 8 through 13:string

String is at index 8,length is 8

String not found


substringToIndex:方法创建了一个子字符串,它包括首字符直到执行的索引数,但是不包括这个字符。因为索引数是从0开始的,所以参数3表示从字符串中提取字符0、1和2,并返回结果字符串对象。对于所有采用索引数作为参数的字符串方法,如果提供的索引数对该字符串无效,就会获得Range or index out of bounds的出错消息。

substringFromIndex:方法返回了一个子字符串,它从接收者的指定索引的字符开始,直到字符串的结尾。

表达式


res=[[str1 substringFromIndex:8]substringToIndex:6];


显示了如何结合这两个方法,提取字符串内部的子字符串。首先使用substringFromIndex:方法从索引数8开始直到字符串结尾的字符;然后对结果应用substringToIndex:方法,以获得前6个字符。最终结果是一个子字符串,它是原字符串中{8,6}范围的字符。

substringWithRange:方法用一步完成我们刚刚用两步所做的工作:它接受了一个范围,并返回指定范围的字符。特殊函数


NSMakeRange(8,6)


根据其参数创建了一个范围,并返回该结果。这个结果可以作为substringWithRange:方法的参数。

要在另一个字符串中查找一个字符串,可以使用rangeOfString:方法。如果在接收者中找到了指定的字符串,则返回的范围精确地指定找到它的位置。然而,如果没有找到这个字符串,则返回范围的location成员被设为NSNotFound。

所以,语句


subRange=[str1 rangeOfString:@“string A”];


把该方法返回的NSRange结构的赋值给NSRange变量subRange。一定要注意subRange不是对象变量,而是一个结构变量(并且程序中的subRange声明不包括星号)。通过使用结构成员操作符(.),可以检索其成员。所以,表达式subRange.location给出了该结构中成员location的值,subRange.length给出该结构中成员length的值。这些值被传递给NSLog函数以显示。