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函数以显示。