疯狂iOS讲义,数组
NSArray代表元素有序、可重复的一个集合。
NSArray的功能与用法
NSArray分别提供了类方法和实例方法来创建NSArray,两种创建方式需要传入的参数基本相似,只是类方法以array开头,而实例方法以init开头,下面是创建Array对象的几类常见方法的介绍。
- array:创建一个不包含任何元素的空NSArray。
- arrayWithContentsOfFile:/initWithContentsOfFile::读取文件内容来创建NSArray。
- arrayWithObject:/initWithObject::创建只包含指定元素的NSArray。
- arrayWithObjects:/initWithObjects::创建包含指定的N个元素的NSArray。
除此之外,还可使用如下简化语法来创建NSArray对象:
@[元素1, 元素2, 元素3, ....]
一旦得到NSArray对象,接下来就可以调用它的方法来操作NSArray集合。NSArray集合最大的特点是集合元素有索引,因此,读者查阅NSArray类的参考手册时会注意到它的绝大部分方法都与集合元素的索引有关。
参考NSArray类的文档,可以看到NSArray集合的方法大致包含如下几类。
- 查询集合元素在NSArray中的索引。
- 根据索引值取出NSArray集合中的元素。
- 对集合元素整体调用方法。
- 对NSArray集合进行排序。
- 取出NSArray集合中的部分集合组成新集合。
所有NSArray的实现类都可以调用这些方法来操作集合元素。下面程序示范了NSArray集合的常规用法。
#import<Foundation/Foundation.h>
int main(int argc, char * argv[]) {
@autoreleasepool{
NSArray* array = [NSArray arrayWithObjects:
@"疯狂iOS讲义", @"疯狂Andriod讲义"
, @"疯狂Ajax讲义", @"疯狂XML讲义"
,@"疯狂Swift讲义", nil];
NSLog(@"第一个元素:%@", [array objectAtIndex:0]);
NSLog(@"索引为1的元素:%@", [array objectAtIndex:1]);
NSLog(@"最后一个元素:%@", [array lastObject]);
//获取从索引为2的元素开始,以及后面的3个元素组成的新集合
NSArray* arr1 = [array objectsAtIndexes: [NSIndexSet
indexSetWithIndexesInRange: NSMakeRange(2, 3)]];
NSLog(@"%@", arr1);
//获取元素在集合中的位置
NSLog(@"疯狂Andriod讲义的位置为:%ld", [array indexOfObject:@"疯狂Andriod讲义"]);
//获取元素在集合指定范围中的位置
NSLog(@"在2~5范围疯狂Andriod讲义的位置为:%ld" ,
[array indexOfObject:@"疯狂Andriod讲义"
inRange:NSMakeRange(2,3)]);//*
//向数组的最后追加一个元素
//原NSArray本身并没有改变,只是将新返回的NSArray赋给array
array = [array arrayByAddingObject:@"孙悟空"];
//向array数组的最后追加另一个数组的所有元素
//原NSArray本身并没有改变,只是将新返回的NSArray赋给array
array = [array arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"宝玉", @"黛玉", nil]];
for(int i = 0 ; i < array.count ; i++) {
NSLog(@"%@", [array objectAtIndex:i]);
//上面代码也可简写为
//NSLog(@"%@", array[i]);
}
//获取array数组中索引为5~8处的所有元素
NSArray* arr2 = [array subarrayWithRange: NSMakeRange(5, 3)];
//将NSArray集合的元素写入文件
[arr2 writeToFile:@"myFile.txt" atomically:YES];
}
return 0;
}
上面程序开始创建了一个NSArray对象,创建NSArray对象时可直接传入多个元素,其中最后一个nil表示NSArray元素结束,其实这个nil元素并不会存入NSArray集合中。
程序遍历NSArray集合时,既可调用NSArray的objectAtIndex:index方法来访问指定索引处的元素,也可直接使用类似于普通数组的下表表示法来访问元素。
也就是说,如下两条代码的作用是相同的。
[array objectAtIndex:i];
array[i[;
很明显,后一种表示方法更加简单、易用。但这个用法只能在iOS 5.0以上的系统中使用,当程序调用array[i]下标形式的语法来访问元素时,实际上就是调用NSArray的objectAtIndexSubscript:方法进行访问。
运行上面的程序,可得到如下结果;
2022-06-05 14:42:04.777193+0800 练习[99094:4210563] 第一个元素:疯狂iOS讲义
2022-06-05 14:42:04.777393+0800 练习[99094:4210563] 索引为1的元素:疯狂Andriod讲义
2022-06-05 14:42:04.777418+0800 练习[99094:4210563] 最后一个元素:疯狂Swift讲义
2022-06-05 14:42:04.777483+0800 练习[99094:4210563] (
“\U75af\U72c2Ajax\U8bb2\U4e49”,
“\U75af\U72c2XML\U8bb2\U4e49”,
“\U75af\U72c2Swift\U8bb2\U4e49”
)
2022-06-05 14:42:04.777505+0800 练习[99094:4210563] 疯狂Andriod讲义的位置为:1
2022-06-05 14:42:04.777520+0800 练习[99094:4210563] 在2~5范围疯狂Andriod讲义的位置为:9223372036854775807
2022-06-05 14:42:04.777543+0800 练习[99094:4210563] 疯狂iOS讲义
2022-06-05 14:42:04.777555+0800 练习[99094:4210563] 疯狂Andriod讲义
2022-06-05 14:42:04.777566+0800 练习[99094:4210563] 疯狂Ajax讲义
2022-06-05 14:42:04.777576+0800 练习[99094:4210563] 疯狂XML讲义
2022-06-05 14:42:04.777589+0800 练习[99094:4210563] 疯狂Swift讲义
2022-06-05 14:42:04.777599+0800 练习[99094:4210563] 孙悟空
2022-06-05 14:42:04.777623+0800 练习[99094:4210563] 宝玉
2022-06-05 14:42:04.777645+0800 练习[99094:4210563] 黛玉
Program ended with exit code: 0
从上面的运行结果可清楚地看出NSArray集合的用法。注意“*”处代码处,程序试图返回“疯狂Andriod“字符串对象在NSArray集合中NSRange(2,3)范围的位置,实际上,该范围内并未包含该字符串对象,因此 程序返回9223372036854775807,这是常量NSNotFound的值。
接着,程序采用两种方式向NSArray集合后面追加了元素。
- 使用arrayByAddingobject:方法追加单个元素。
- 使用arrayWithObjects:方法将另一个数组中所有的元素追加到原数组的后面。
不管使用上面那种方法,对于原有的NSArray对象都不会产生任何修改(因为NSArray集合本身是不能修改的),程序只是返回了一个新的NSArray对象。
NSArray如何判断集合是否包含指定元素
标准只有一条:只有某个集合元素与被查找元素通过isEqual:方法比较返回YES,即可认为该NSArray集合包含该元素,并不需要两个元素是同一个元素。
为了证明NSArray的比较机智,程序先定义了一个FKUser类,该类的接口部分代码如下。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface FKUser : NSObject
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* pass;
- (id) initWithName:(NSString*) aName
pass:(NSString*) aPass;
- (void) say:(NSString*) content;
@end
NS_ASSUME_NONNULL_END
上面的FKUser类只定义了name、pass两个属性,接下来FKUser类将会重写isEqual方法,重写该方法的比较标准是,如果两个FKUser的name、pass相等,即可认为它们相等。FKuser类的实现部分代码如下。
#import "FKUser.h"
@implementation FKUser
@synthesize name;
@synthesize pass;
- (id) initWithName:(NSString *) aName
pass:(NSString *) aPass {
if(self = [super init]) {
name = aName;
pass = aPass;
}
return self;
}
- (void) say:(NSString *) content {
NSLog(@"%@说:%@", self.name, content);
}
//重写isEqual:方法,重写该方法的比较标准是
//如果两个FKUser的name、pass相等,即可认为它们相等
- (BOOL) isEqual:(id) other {
if(self == other) {
return YES;
}
if([other class] == FKUser.class) {
FKUser* target = (FKUser*)other;
return [self.name isEqual:target.name] && [self.pass isEqual:target.pass];
}
return NO;
}
//重写description方法,可以看到FKUser对象的状态
- (NSString*) description {
return [NSString stringWithFormat:@"<FKUser[name=%@,pass=%@]>", self.name, self.pass];
}
@end
由于程序希望直接看到FKUser对象的内部状态,因此,程序还为了FKUser类重写了description方法。
如下程序示范了NSArray集合中查找指定的FKUser对象。
#import<Foundation/Foundation.h>
#import"FKUser.h"
int main(int argc, char * argv[]) {
@autoreleasepool{
NSArray* array = [NSArray arrayWithObjects:
[[FKUser alloc]initWithName:@"sun" pass:@"123"],
[[FKUser alloc]initWithName:@"bai" pass:@"345"] ,
[[FKUser alloc]initWithName:@"zhu" pass:@"654"],
[[FKUser alloc]initWithName:@"tang" pass:@"178"],
[[FKUser alloc]initWithName:@"niu" pass:@"155"],
nil];
//查找指定新FKUser对象在集合中的索引
FKUser* newUser =[[FKUser alloc]initWithName:@"zhu" pass:@"654"];
NSUInteger pos = [array indexOfObject:newUser];
NSLog(@"newUser的位置为:%ld", pos);
}
return 0;
}
上面程序中开始处使用了简化语法来创建NSArray对象,创建NSArray时直接指定了集合元素。程序中粗体字代码查找一个新的FKUser对象在NSArray集合中的索引。很明显,这是一个全新的FKUser对象,虽然该对象与集合中的任何对象都不相同,但程序依然可以返回该FKUser对象在该集合中的索引,这是因为该FKUser对象的name、pass与集合中第3个元素的name、pass相等,因此程序返回这个新的FKUser对象在集合中的索引应为2.
对集合元素整体调用方法
如果是简单地调用集合元素的方法,可通过NSArray的如下两种方法来实现。
- makeObjectsPerformSelector::依次调用NSArray集合中每个元素的指定方法,该方法需呀传入一个SEL参数,用于指定调用哪个方法。
- makeobjectsPerformSelector:withObject::依次调用NSArray集合中每个元素的指定方法,该方法的第一个SEL参数用于指定调用哪个方法,第二个参数用于调用集合元素的方法时传入参数。
如果希望对集合中的所有元素进行隐式遍历,并使用集合元素来执行某段代码,则可通过NSArray的如下方法来完成。- enumerateObjectsUsingBlock::遍历集合中的所有元素,并依次使用元素来执行指定的代码块。
- enumeraObjectsWithOption:usingBlock:遍历集合中的所有元素,并依次使用元素来执行指定的代码块。该方法可以额外传入一个参数,用于控制遍历的选项,如反向遍历。
- enumerateObjectsAtIndexes:options:usingBlock::遍历集合中的所有元素,并依次使用元素来执行指定的代码块。该方法可传入一个选项参数,用于控制遍历的选项,如反向遍历。
上面3个方法都需要传入一个代码块参数,该代码块必须带3个参数,第1个参数代表正在遍历的集合元素,第2个参数代表正在遍历的集合元素的索引,第三个参数就是用于遍历集合元素的代码块。
如下程序示范了如何对集合元素整体调用方法。
#import<Foundation/Foundation.h>
#import"FKUser.h"
int main(int argc, char * argv[]) {
@autoreleasepool{
//使用简化语法创建NSArray对象
NSArray* array = @[
[[FKUser alloc] initWithName:@"sun" pass:@"123"],
[[FKUser alloc] initWithName:@"bai" pass:@"345"],
[[FKUser alloc] initWithName:@"zhu" pass:@"654"],
[[FKUser alloc] initWithName:@"tang" pass:@"178"],
[[FKUser alloc] initWithName:@"niu" pass:@"155"],
];
//对集合元素整体调用方法
[array makeObjectsPerformSelector:@selector(say:) withObject:@"下午好,NSArray真强大!"];
NSString* content = @"疯狂iOS讲义";
//迭代集合内指定范围内的元素,并使用该元素来执行代码块
[array enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 2)]
options:NSEnumerationReverse
usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"正在处理第%ld个元素:%@", idx, obj);
[obj say:content];
}];
}
return 0;
}
上面程序中第一行粗体字代码直接调用NSAArray的makeObjectsPerformSelector:方法第一所有集合的say:方法,并通过withObject标签为say:方法传入参数。
第二段粗体字代码略微复杂一些,它调用了NSArray的enumerateObjectsAtIndexes:options:usingNlock:方法,这个方法用于遍历NSArray集合中指定索引范围的元素,并用这些元素来执行指定的代码块,传入的代码块参数就代表程序对每个回合元素迭代执行的代码体。
编译、运行该程序,可以看到如下结果:
2022-06-05 22:37:43.758919+0800 练习[3047:4386493] sun说:下午好,NSArray真强大!
2022-06-05 22:37:43.759176+0800 练习[3047:4386493] bai说:下午好,NSArray真强大!
2022-06-05 22:37:43.759197+0800 练习[3047:4386493] zhu说:下午好,NSArray真强大!
2022-06-05 22:37:43.759213+0800 练习[3047:4386493] tang说:下午好,NSArray真强大!
2022-06-05 22:37:43.759227+0800 练习[3047:4386493] niu说:下午好,NSArray真强大!
2022-06-05 22:37:43.759268+0800 练习[3047:4386493] 正在处理第3个元素:<FKUser[name=tang,pass=178]>
2022-06-05 22:37:43.759284+0800 练习[3047:4386493] tang说:疯狂iOS讲义
2022-06-05 22:37:43.759300+0800 练习[3047:4386493] 正在处理第2个元素:<FKUser[name=zhu,pass=654]>
2022-06-05 22:37:43.759313+0800 练习[3047:4386493] zhu说:疯狂iOS讲义
Program ended with exit code: 0
从上面的执行结果可以看出,NSArray既可对集合中所有的元素整体调用用某个方法,也可通过NSIndexSet来控制只对集合中部分元素迭代调用指定的代码块。虽然程序只是调用NSArray的方法,但程序在执行这些方法时,底层实际上会使用循环进行迭代处理调用每个集合元素,因此,执行方法都可以称为迭代方法。
对NSArray进行排序
NSArray提供了大量的方法对集合元素进行排序,这些排序方法都以sorted开头,最常用的排序方法如下:
#import<Foundation/Foundation.h>
//定义比较函数,根据两个对象的intValue进行比较
NSComparisonResult 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{
//初始化一个元素为NSString的NSArray对象
NSArray* array1 = @[@"Objective-C", @"C", @"C++", @"Ruby", @"Perl", @"Swift"];
//使用集合元素的compare:方法进行排序
array1 = [array1 sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"%@", array1);
//初始化一个元素为NSNumber的NSArray对象
NSArray* array2 = @[ [NSNumber numberWithInt:20],
[NSNumber numberWithInt:12],
[NSNumber numberWithInt:-8],
[NSNumber numberWithInt:50],
[NSNumber numberWithInt:19],
];
//使用intSort函数进行排序
array2 = [array2 sortedArrayUsingFunction:intSort context:nil];
NSLog(@"%@", array2);
//使用代码块对集合元素进行排序
NSArray* array3 = [array2 sortedArrayUsingComparator:
^ (id obj1, id obj2)
{
//该代码块就是根据集合元素的intValue进行比较
if([obj1 intValue] > [obj2 intValue]) {
return NSOrderedDescending;
}
if([obj1 intValue] < [obj2 intValue]) {
return NSOrderedAscending;
}
return NSOrderedSame;
}];
NSLog(@"%@", array3);
}
return 0;
}
上面程序中的3行粗体字代码分别示范了对NSArray进行排序的3种方法,其中第一种方法使用NSString自身的compare:方法进行排序。这是因为NSString自身已经实现了compare:方法,这意味着NSString对象本身就可以比较大小——NSString比较大小的方法是根据字符对应的编码来进行的。
后面两种方法通过调用函数或代码块来比较大小,代码块相当于一个匿名函数,因此后面两种方法的本质时一样的,它们都可以通过自定义的比较规则来比较集合元素的大小——不管集合元素本身是否可以比较大小,只要程序通过比较函数或代码块定义自己的比较规则即可。
使用枚举器遍历NSArray集合元素
对于NSArray对象,除了可根据集合元素的索引来遍历集合元素之外,还可以调用NSArray对象的如下两个方法来返回枚举器。
- objectEnumerator:返回NSArray集合的顺序枚举器。
- reverseObjectEnumerator:返回NSArray集合的逆序枚举器。
上面两个方法都返回一个NSEnumerator枚举器,该枚举器只包含如下两个方法。 - allObjects:获取被枚举集合的所有元素。
- nextObject:获取被枚举集合中的下一个元素。
一般来说,借助nextObject方法即可对集合元素进行枚举:程序可采用循环不断获取nectObject方法的返回值,知道该方法的返回值为nil结束循环。
使用NSEumerator遍历集合元素的示范程序如下。
#import<Foundation/Foundation.h>
int main(int argc , char * argv[]) {
@autoreleasepool {
//读取前面写入磁盘的文件,用文件内容来初始化NSArray集合
NSArray * array = [NSArray arrayWithContentsOfFile:@"myFile.txt"];
//获取NSArray的顺序枚举器
NSEnumerator* en = [array objectEnumerator];
id object;
while(object = [en nextObject]) {
NSLog(@"%@", object);
}
NSLog(@"------下面逆序遍历------");
//获取NSArray的逆序枚举器
en = [array reverseObjectEnumerator];
while(object = [en nextObject]) {
NSLog(@"%@", object);
}
}
}
2022-06-06 18:42:36.287946+0800 练习[12116:4762436] 孙悟空
2022-06-06 18:42:36.288127+0800 练习[12116:4762436] 宝玉
2022-06-06 18:42:36.288144+0800 练习[12116:4762436] 黛玉
2022-06-06 18:42:36.288174+0800 练习[12116:4762436] ------下面逆序遍历------
2022-06-06 18:42:36.288232+0800 练习[12116:4762436] 黛玉
2022-06-06 18:42:36.288255+0800 练习[12116:4762436] 宝玉
2022-06-06 18:42:36.288269+0800 练习[12116:4762436] 孙悟空
Program ended with exit code: 0
快速枚举
OC还提供了一种快速枚举的方法来遍历集合(包括NSArray、NSSet、NSDictionary等),使用快速枚举遍历集合元素时,无须获得集合的长度,也无须根据索引来访问集合元素,即可快速枚举自动遍历集合的每个元素。
快速枚举的语法格式如下;
for(type variableName in collection) [
//variableName自动叠戴访问每个元素...
}
在上面的语法格式中,type时集合元素的类型,varibaleName是一个形参名,快速枚举将自动将集合元素依次赋给该变量。
如果使用快速枚举来遍历NSDictionary对象,那么快速枚举中循环计数器将依次代表NSDiction的每个key的值。
下面程序示范了如何使用快速枚举来遍历NSArray集合的元素。
#import<Foundation/Foundation.h>
int main(int argc , char * argv[]) {
@autoreleasepool {
//读取前面写入磁盘的文件,用文件内容来初始化NSArray集合
NSArray* array = [NSArray arrayWithContentsOfFile:@"myFile.txt"];
for(id object in array) {
NSLog(@"%@", object);
}
}
return 0;
}
从上面的程序可以看出,使用快速枚举遍历元素无须获得数组长度,也无须根据索引来访问数组元素。快速枚举的本质是一个for-each循环,for-each循环和普通循环不同的是,它无须循环条件也无须循环迭代语句,这些部分都有系统来完成,for-each循环自动迭代数组的每个元素,当每个元素都被迭代一次后,for-each循环自动结束。
可变数组(NSMutableArray)
NSArray代表集合元素不可变的集合,一旦NSArray创建成功,程序就不能向集合中添加新的元素,不能删除集合中已有的元素,也不能替换集合元素。
NSArtray只是保存对象的指针,因此,NSArray只保证这些指针变量中的地址不能被改变,但指针变量所指向的对象是可以改变的。
NSArray有一个子类:NSMutableArray,因此它可以作为NSArray使用。于此同时,它代表的是一个集合元素可变的集合,因此程序可以向集合中添加新的元素,可以删除集合中已有的元素,也可以替换集合元素。
NSMutableArray代表集合元素可变的集合,而NSMutableArray底层采用传统数组来容纳集合元素,因此,创建NSMutableArray时可通过参数指定底层数组的初始容量。
NSMutableArray主要新增了如下方法:
- 添加集合元素的方法:这类方法以add开头。
- 删除集合元素的方法:这类方法以remove开头。
- 替换集合元素的方法:这类方法以replace开头。
- 对集合本身排序的方法:这类方法以sort开头。
下面程序示范了如何改变NSMutableArray集合中的元素。
#import<Foundation/Foundation.h>
//定义一个函数,该函数用于把NSArray集合转变为字符串
NSString* NSCollectionToString(NSArray* array) {
NSMutableString* result = [NSMutableString stringWithString:@"["];
for(id obj in array) {
[result appendString:[obj description]];
[result appendString:@", "];
}
NSUInteger len = [result length];//获取字符串长度
//去掉字符串最后的两个字符
[result deleteCharactersInRange:NSMakeRange(len-2, 2)];
[result appendString:@"]"];
return result;
}
int main(int argc , char * argv[]) {
@autoreleasepool {
//读写前面被写入磁盘的文件,用文件内容来初始化NSMutableArray集合
NSMutableArray* array = [NSMutableArray
arrayWithContentsOfFile:@"myFile.txt"];
[array addObject:@"疯狂iOS讲义"];//向集合最后添加一个元素
NSLog(@"最后追加一个元素后:%@", NSCollectionToString(array));
//使用NSArray向集合尾部添加多个元素
[array addObjectsFromArray:[NSArray
arrayWithObjects:@"张飞", @"关羽", nil]];
NSLog(@"最后追加两个元素后:%@", NSCollectionToString(array));
//向集合指定位置插入一个元素
[array insertObject:@"疯狂iOS讲义" atIndex:2];
NSLog(@"在索引为2的位置插入一个元素后:%@", NSCollectionToString(array));
//使用NSArray向集合指定位置插入多个元素
[array insertObjects:[NSArray
arrayWithObjects:@"武松", @"林冲", nil]
atIndexes:[NSIndexSet indexSetWithIndexesInRange
:NSMakeRange(3, 2)]];
NSLog(@"插入多个元素后:%@", NSCollectionToString(array));
[array removeLastObject];//删除集合最后一个元素
NSLog(@"删除最后一个元素后:%@", NSCollectionToString(array));
[array removeObjectAtIndex:5];//删除指定索引处的元素
NSLog(@"删除索引为5处的元素后:%@", NSCollectionToString(array));
[array removeObjectsInRange:NSMakeRange(2, 3)];//删除索引为2~4处的元素
NSLog(@"删除索引为2~4处的元素后:%@", NSCollectionToString(array));
[array replaceObjectAtIndex:2 withObject:@"疯狂iOS讲义"];//替换索引为2处的元素
NSLog(@"替换索引为2处的元素后:%@", NSCollectionToString(array));
}
return 0;
}
上面程序先定义了一个NSCollectionToString()函数,该函数可以吧NSArray集合转换为字符串,只要方便调试时看到NSArray集合中的元素。
接下来分别调用了NSMutableArray的addXxx、removeXxx、replaceXxx方法向集合中添加元素、删除元素、替换元素。执行上面的程序,可以看到如下输出:
通过上面的程序不难看出,NSMutableArray与NSArray相比,最大的区别就是增加了用于“增加元素”、“删除元素”、“替换元素”的3种方法。