NSArray的功能与用法
NSArray概述
NSArray简单地来讲就是数组,但是它比数组更高级,它是一个OC封装的具有很多强大功能的类。
NSArray代表元素有序、可重复的集合,集合中每一个元素都有其对应的顺序索引,和数组一样,它的索引也是从0开始。
NSArray使用
NSArray提供了类方法和示例方法来创建NSArray。两种方式的传入参数基本类似。
类方法以array开头,而实例方法以init开头,以下是几种常见的方法:
1. array: 创建一个不包含任何元素的空NSArray;
2. arrayWithContentsOfFile:/initWithContentsOfFile:读取文件内容来创建NSArray;
3. arrayWithObject:/initWithObject:创建只包含指定元素的NSArray;
4. arrayWithObjects:/initWithObjects:创建之包含指定n个元素的NSArray;
创建完成之后,我们就可以开始使用它的方法了!
这种数据结构的特点在于它的索引,因此该类提供的方法中大多数也是与索引有关联。参考NSArray的源代码,我们不难发现其实方法大概分为这么集中类别:
1. 查询集合中某个元素在NSArray中的索引值;
2. 根据索引值取出NSArray中的元素;
3. 对集合元素整体调用方法;
4. 对NSArray集合进行排序;
5. 取出NSArray中的部分元素组成新的集合;
那么下面给一个例子来示范以下NSArray的常规用法(注意要在头文件中引入Foundation/Foundation.h头文件):
- (void) NSArrayDemo
{
NSArray* array = [NSArray arrayWithObjects:
@"Mike", @"Hudson", @"Colin", @"Steve", nil];
//输出索引为0的元素
NSLog(@"The first element is:%@", [array objectAtIndex:0]);
//输出末尾元素
NSLog(@"The last element is: %@", [array lastObject]);
//取出从索引1开始(包括1),后面2个元素组成的集合
NSArray* resultArray = [array objectsAtIndexes:
[NSIndexSet indexSetWithIndexesInRange: NSMakeRange(1,2)]];
NSLog(@"%@", resultArray);
//取出某个元素在集合中的位置
NSLog(@"The index of 'Colin' in this array is : %ld",
[array indexOfObject: @"Colin"]);
//获取某个元素在制定范围中的位置
NSLog(@"The index of 'Hudson' in range 2 to 3 is: %ld",
[array indexOfObject:@"Hudson" inRange:NSMakeRange(2,1)]);
//向数组的末尾追加一个元素
array = [array arrayByAddingObject:@"David"];
//向数组的最后追加另一个数组的所有元素,原来的NSArray并没有发生改变,只是将新返回的NSArray赋值给array
array = [array arrayByAddingObjectsFromArray: [NSArray arrayWithObjects: @"Weiss", @"Lannister", nil]];
for(id object in array){
NSLog(@"%@", object);
}
//获取array数组中2到5的元素
NSArray* array2 = [array subarrayWithRange:NSMakeRange(2,3)];
//将array2写入到文件
[array2 writeToFile:@"NSArrayTest.txt" atomically:YES];
}
注:上面的代码中,用到了nil作为初始化的结尾对象,它是一个结束标志,并不会被记录到数组当中;
上面的代码还用到了一个NSIndexSet集合,它的功能和NSSet基本相似,区别是NSIndexSet主要用来保存索引值,因此它的集合元素都是NSUInteger对象;
数组访问可以使用方法objectAtIndex:index,也可以直接采用和C/C++语言类似的下标访问,它们是类似的。
以上代码会有一个地方输出的结果非常奇怪,9223372036854775807,这是因为要查找的索引并不存在,所以返回的值是常量NSNotFound的数值。
一个非常重要的问题!!:
在上面代码中,我们进行了一些查找字符串所在的索引的实验,并且我们在每次调用查找的时候给出了我们要查找的参数。查找过程非常顺利正常,@"Colin" 和 @"Colin" 相等当然是非常合理、正确的。然而,如果在这个数组里面装着的是我们自己定义的类型,那么NSArray如何去知道某个对象是或不是我们要寻找的呢?
其实很简单,在OC的基础类NSObject中有一个方法isEqual:(id),它就是用来决定两个对象到底是相等还是不相等的。所以,如果我们希望NSArray能够识别出来我们自己写的类的两个实例到底在什么情况下相等,那么我们就要去重写这一个方法,重写的步骤也非常简单,下面给一个示例,假设我们有一个类叫做Book,它有两个属性,name和publisher,都是NSString*类型,下面这个方法就会把名称相同,出版社相同的书认为是相等的:
- (BOOL) isEqual: (id) other
{
//指向的是同一个对象的地址空间,返回真
if(self == other){
return YES;
}
//判断所属类是否相同
if([other class] == Book.class){
Book* target = (Book*)other;
return [self.name isEqualToString: target.name]
&&[self.publisher isEqualToString: target.publisher];
}
return NO;
}
对集合元素整体调用方法
NSArray允许对集合中所有的元素或部分元素整体调用方法:
1. makeObjectsPerformSelector: 依次调用NSArray集合中每个元素的指定方法,传入参数是SEL,用于指定被调用的方法;
2. makeObjectsPerformSelector: withObject: 依次调用NSArray集合中每个元素的指定方法,参数SEL用于指定调用的方法,第二个参数用于传入调用方法参数,第三个参数用于控制是否要中止迭代,如果在处理某个元素之后,将第三个元素赋值为YES,那么方法就会中止迭代调用;
如果希望对集合中的所有元素进行隐式遍历,并使用集合元素来执行某一段代码,可以通过以下方法来完成:
1. enumerateObjectsUsingBlock:
遍历集合中的所有元素,并使用元素来执行指定的代码块;
2. enumerateObjectsWithOptions: usingBlock:
遍历集合中的所有元素,并依次使用元素来执行指定的代码块,该方法可以传入一个选项参数,用于控制遍历的选项,例如正向还是反向遍历;
3. enumerateObjectsAtIndexes: options: usingBlock:
多出来的一个参数可以用来控制遍历的范围;
以上3个方法都需要一个代码块参数,该代码块必须包括3个参数,前一个表示正在遍历的集合元素,第二个参数表示正在遍历的元素的索引,下面是一个示例:
//下面是一个要在演示的方法中使用到的类的定义以及实现文件
#import <Foundation/Foundation.h>
@interface NSBook : NSObject
@property(nonatomic, copy) NSString* name;
@property(nonatomic, copy) NSString* author;
- (id) initWithName:(NSString*) bookName author: (NSString*) bookAuthor;
- (void) tellBookInfo:(NSString*) note;
@end
//实现部分
//implementation
#import "NSBook.h"
@implementation NSBook
@synthesize name;
@synthesize author;
- (id) initWithName:(NSString *) bookName author:(NSString *) bookAuthor
{
if(self = [super init]){
self.name = [bookName mutableCopy];
self.author = [bookAuthor mutableCopy];
}
return self;
}
- (void) tellBookInfo:(NSString*) note
{
NSLog(@"Book Name: %@", self.name);
NSLog(@"Book Author: %@", self.author);
NSLog(@"%@", note);
}
- (NSString*) description
{
return [NSString stringWithFormat:@"Book:[name = %@; author = %@]", self.name, self.author];
}
@end
下面就是方法主体的演示部分;
#import <Foundation/Foundation.h>
#import "NSBook.h"
@interface NSArrayDemo : NSObject
- (void) NSArrayObjectsDemo;
@end
#import "NSArrayDemo.h"
@implementation NSArrayDemo
- (void) NSArrayObjectsDemo
{
NSArray* array = [NSArray arrayWithObjects:
[[NSBook alloc] initWithName:@"Game of Thrones" author:@"George R.R Martin"],
[[NSBook alloc] initWithName:@"The Clash of King" author:@"George R.R Martin"],
[[NSBook alloc] initWithName:@"A Dance with Dargons" author:@"George R.R Martin"], nil];
//对集合中每一个元素都执行方法
[array makeObjectsPerformSelector:@selector(tellBookInfo:)
withObject:@"my note"];
NSString* content = @"reading!";
//迭代集合指定范围内的元素,并使用该元素作为参数执行代码块
[array enumerateObjectsAtIndexes:
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,2)]
options: NSEnumerationReverse
//代码块的第一个参数是正在迭代中的集合元素
//第二个参数是正在遍历的集合元素的索引
usingBlock: ^(id obj, NSUInteger idx, BOOL *stop)
{
NSLog(@"正在处理第%ld个元素: %@", idx, obj);
[obj tellBookInfo:content];
}];
}
@end
对NSArray中的元素进行排序
NSArray提供了大量的方法对集合的元素进行排序,它们基本都以单词sort开头,最常用的排序方法如下:
sortedArrayUsingFunction: context:
该方法使用排序函数对集合的元素进行排序,该排序函数必须返回NSOrderedDescending / NSOrderedAscending / NSOrderedSame 这一类的枚举值用于表示集合元素的大小, 该方法返回一个排好序的NSArray对象;sortedArrayUsingSelector:
该方法使用集合元素自身的方法对集合的元素进行排序, 集合元素的方法必须返回NSOrderedDescending / NSOrderedAscending / NSOrderedSame 枚举值当中的一个, 该方法同样返回一个排好序的NSArray;sortedArrayUsingComparator:
该方法使用代码块对集合的元素进行排序,同样,这个代码块还是要返回三个枚举值之一来表示元素的大小关系。该方法的返回值同样是一个排好序的NSArray;
//定义一个比较函数,根据整数的数值进行比较
NSInteger intSort(id num1, id num2, void* context){
int v1 = [num1 intValue];
int v2 = [num2 intValue];
if(v1 < v2){
return NSOrderedAscending;
}else if(v1 > v2){
return NSOrderedDescending;
}else{
return NSOrderedSame;
}
}
int main(int argc, char * argv[]){
@autoreleasepool{
NSArray* array = [NSArray arrayWithObjects:@"Objective-C", @"PHP", @"C++", @"Python", @"Java", @"Andriod", @"Ruby", nil];
//使用集合元素的compare: 方法进行排序
array = [array sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"%@", array);
//初始化一个元素为NSNumber的NSArray对象
NSArray* array1 = [NSArray arrayWithObjects:
[NSNumber numberWithInt:45],
[NSNumber numberWithInt:75],
[NSNumber numberWithInt:24],
[NSNumber numberWithInt:36],
[NSNumber numberWithInt:57],
nil];
array1 = [array1 sortedArrayUsingFunction:intSort context:nil];
NSLog(@"%@", array1);
//使用代码块进行排序
NSArray* array2 = [array1 sortedArrayUsingComparator:
^(id obj1, id obj2)
{
if([obj1 intValue] > [obj2 intValue]){
return NSOrderedDescending;
}else if([obj1 intValue] < [obj2 intValue]){
return NSOrderedAscending;
}else{
return NSOrderedSame;
}
}];
NSLog(@"%@", array2);
}
return 0;
}
以上示例中,第一个排序方法用到了NSString类自带的compare:方法来进行比较,后面两个方法则使用了自定义的方法和自定义的代码块来进行元素的比较,这比较类似于java中改写某个类的compare方法。
使用枚举器遍历NSArray集合的元素
对于NSArray对象,除了可以根据集合元素的索引来遍历集合元素之外,还可以调用NSArray对象的下面两个方法来返回枚举器:
- objectEnumerator:
返回NSArray集合的顺序枚举器; - reverseObjectEnumerator:
返回NSArray的逆序枚举器;
这连个方法都返回枚举器,枚举器只有如下两个方法:
allObjects: 获取被枚举的集合中的所有元素;
nextObjects: 获取被枚举集合中的下一个元素;
一般来说,借助nextObject方法就可以对集合的元素进行枚举了,下面给出一个示例,实例中的代码直接填写到主函数中就可以演示了:
NSArray* array = [NSArray arrayWithObjects:@"Objective-C", @"PHP", @"C++", @"Python", @"Java", @"Andriod", @"Ruby", nil];
NSEnumerator* en = [array objectEnumerator];
id object;
while(object = [en nextObject]){
NSLog(@"%@", object);
}
NSLog(@"下面是逆序遍历的结果");
NSEnumerator* ren = [array reverseObjectEnumerator];
while(object = [ren nextObject]){
NSLog(@"%@", object);
}
快速枚举(for … in)
Objective C还提供了另外一种快速枚举的办法来遍历集合,它类似于java中的加强for循环,可以应用于NSArray、NSSet、NSDictionary等集合,快速枚举的语法格式如下:
for(type variableName in collection){
//variableName自动迭代访问每个元素
}
其中,type是集合元素变量的类型,variableName是形参名,快速枚举自动将集合的元素依次赋值给这个变量
注:如果利用快速枚举自动遍历NSDictionary对象,那么快速枚举中循环计数器一次代表NSDictionary的每一个key值。