1.概念
OC集合类是一些非常有用的工具类,他可以用于存储多个数量不等的对象,并可以实现常用的数据结构,如栈、队列等。除此之外还可以用于保存具有映射关系的关联数组。OC的集合大致上可以分为NSArray、NSSet、NSDictionary三种体系
- NSArray:代表有序,可重复的集合
- NSSet:代表无序,不可重复的集合
- NSDictionary:代表具有映射关系的集合
- OC集合就像一种容器,程序可以把多个对象(实际上是指针)都存进该容器中
- 为了保存不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),OC提供了集合类,分别由NSArray、NSSet、NSDictionary这三个类簇代表,实际上也可能是他们的子类的实例(NSMutableArray、NSMutableSet、NSMutableDictionary)
- 集合与数组不同,数组中既可以保存基本类型的值,也可以是对象(指针变量);而集合里只能保存对象(指针变量)
2.特征
- NSSet集合类似一个罐子,将一个对象添加到NSSet集合时,NSSet集合无法记住添加这个元素的顺序,所以NSSet里的元素不可以重复;
- NSArray集合非常像一个数组,他可以记住每次添加元素的顺序;只是NSMutableArray的长度可变
- NSDictionary集合也像一个罐子,他里面的每项数据都由两个值组成
从图中可以看出,如果访问NSArray集合中的元素,可以直接根据元的索引来访问;如果需要访问NSDictionary集合中的元素,则可以根据每项元素的key值来访问其中的value;如果希望访问NSSet集合中的元素,只能根据元素本身来访问
NSArray的功能与用法
- NSArray分别提供了类方法与实例方法来创建NSArray,两种创建方式需要传入的参数基本相似,只是类方法以array开头,实例方法以init开头
- array:创建一个不包含任何元素的空NSArray
- arrayWithContensOfFile:或者initWithContentsOfFile: :读取文件内容来创建NSArray
- NSBundle是OC中用于管理应用程序资源的类,应用程序的资源包括很多东西,比如plist文件、图像文件、nib文件、sqlite等数据库文件,甚至一些自定义的文本文件等
- MainBundle是NSBundle的一个类方法,[NSBundle minaBundle]返回的是应用程序的主资源包对象,主资源包包含了应用程序在安装时自带的资源文件(例如,应用的图标,默认的配置文件等都存储在主资源包中)
pathForResource:ofType:
是NSBundle
类的一个实例方法。当你拥有一个NSBundle
对象(比如[NSArray mainBundle]
返回的主资源包对象)后,可以调用这个方法来获取指定资源文件的路径具体来说:
pathForResource:@"data" ofType:@"plist"
中的@"data"
是资源文件的名称(不包含文件扩展名)。@"plist"
是文件的类型(扩展名)。举例:
假设在 Xcode 项目中添加了一个名为
data.plist
的文件到主资源包中,当你调用[[NSBundle mainBundle] pathForResource:@"data" ofType:@"plist"]
时,它会返回data.plist
文件在应用程序沙盒中的具体路径,比如/Users/.../AppName.app/data.plist
这样的路径字符串。之后,你就可以根据这个路径去读取文件内容,例如创建NSArray
(如果data.plist
的内容是数组格式)等操作
-
arrayWithObject:或者initWithIbject: :创建只包含指定元素的NSArray
-
arrayWithObjects: 或者initWithObjects: :创建包含指定的N个元素的NSArray
-
除此之外还可以使用简化版语法来创建数组
```c @[元素1,元素2,元素3,.....] ```
NSArray* emptyArray = [NSArray array];
//简化语法 NSArray* emptyArray = @[];
NSString* str = @"hello";
NSArray* singleArray = [NSArray arrayWithObject:str];
//简化语法:NSArray* singleArray = @[str];
NSArray* mutiArray = [NSArray arrayWithObjects:@"apple",@"cherry", nil];
//简化语法:NSArray* mutiArray = @[@"apple",@"banana",@"cherry"];
一旦得到NSArray对象,接下来就可以调用他的方法来操作NSArray集合
//nil表示NSArray元素结束,其实这个nil并不会存入NSArray集合中。
NSArray* array = [NSArray arrayWithObjects:@"hello",@"laoda",@"what can i say",nil];
- 查询集合元素在NSArray中的索引
[array indexOfObject:@"---"]
- 获取元素在集合指定范围中的位置
[array indexOfObject:@"hello" inRange:NSMakeRange(2,3)];
//如果该范围没有包含该字符串对象,程序会返回9223372036854775807,这是常量NSNotFound的值
- 获取NSArray集合中指定范围的元素
NSArray* arr = [array subarrayWithRange:NSMakeRange(5,3)];
- 查询对应索引的元素
[array objectAtIndex:0]
[array lastObject]
- 向数组最后追加一个元素(不管是哪种追加,都不会对原有的NSArray产生任何修改,因为NSArray本身是个不能进行修改的)程序只是返回一个新的NSArray对象
[array arrayByAddingObject:@"World"];
arrayWithObjects:
- 向array数组的最后追加另一个数组的所有元素
array = [array arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"OO",@"PP",@"QQ",nil]];
- 遍历NSArray数组
for (int i = 0; 9 < array.count; i++) {
NSLog(@"%@",[array objectAtIndex:i];
//NSLog(@"%@",array[i];
}
- 将NSArray集合的元素写入文件
[arr writeToFile:@"myFile.txt" atomically:YES];
- 取出NSArray集合中的部分集合组成的新集合
[array objectsAtIndex:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2,3)]];
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2,3)];
NSIndexSet是一个表示索引集合的类,用与表示一组索引
NSindexSet集合元素元素都是NSInteger对象
indexSetWithIndexesInRange: 是NSIndexSet的类方法,他接受一个NSRange作为参数,并返回一个包含指定范围内索引的NSIndexSet对象,所以:[NSIndexSet indexSetWithindexesInRange:NSMakeRange(2,3)];创建了一个NSIndexSet对象,该对象包含从索引2到索引4,即2,3,4;
[array objectsAtIndex:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 3)]]
array
是一个NSArray
类型的对象(假设是一个数组)。objectsAtIndex:
是NSArray
的实例方法,它接受一个NSIndexSet
对象作为参数。- 这行代码的作用是从
array
数组中获取由NSIndexSet
指定索引对应的元素,并返回一个新的数组,新数组包含了原数组中指定索引位置的元素。例如,如果array
数组包含多个元素,[array objectsAtIndex:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 3)]]
会返回一个新数组,这个新数组包含了array
数组中索引为2
、3
、4
的元素。
- 程序是如何判断数组中是否包含指定元素的?
标准:只有某个集合元素与被查找元素通过isEqual:方法比较返回Yes时,才可以认为NSArray集合中包含该元素,并不需要两个元素是同一个元素
举例:
#import<Foundation/Foundation.h>
@interface FKUser : NSObject
@property (nonatomic, copy)NSString* name;
@property (nonatomic, copy)NSString* pass;
- (id) initWithName:(NSString*)aName Withpass:(NSString*)aPass;
- (void) say:(NSString*)content
@end;
FKUser类重写isEqual:方法,标准是如果两个FKUser对象的name与pass相同,就认为他们相等
#import"FKUser.h"
@implemention FKUser
- (id) initWithName:(NSString*)name Withpass"(NSString*)aPass
{
if (self = [super init]) {
self->_name = aName;
self->_pass = aPass;
}
return self;
}
- (void)say:(NSString*)content
{
NSLog(@"%@ say: @",self.name,content);
}
//重isEqqual方法的比较标准是:两个name与pass想的那就想等
- (BOOL) isEqual:(id)other
{
if (self == other) {
return YES;
}
if ([other class] == FKUser.class) {
FKUser* target = (FKUser*)other;
return [self.name idEqualToAtring:target.name] && [self.pass isEqualToString:target.pass];
}
return NO;
}
//重写了description方法,可以直接看到FKUser对象的状态
- (NSString*)description {
return [NSString stringWithObjects:@"<FKUser[name = %@, pass = %@]>",self.name,self.pass];
}
@end
解释重写方法:
- 首先检查两个对象是否指向同一块内存地址,如果是直接返回yes
- 检查others是否为当前FKUser类的实例,如果不是就不进入循环,返回NO;
- 强制转换,以便访问其name与pass属性
- 虽然运行通过了判断语句,class确认了other是FKUser的实例,但是编译器在编译时只知道other是id类型(泛型对象)
- 编译器不允许通过id类型直接访问FKUser的name与pass属性(因为id类型不保证有这些属性),强制转换后编译器才会认可这些属性的访问
- 使用isEqualToString:判断两个字符串内容是否相同
对集合整体元素调用方法
NSArray允许对集合中的所有或者部分元素整体调用方法,如果只是简单的调用集合元素的方法·,则可以通过NSArray的如下两种方法进行调用:
- 依次调用NSArray集合中的每个元素的指定方法,该方法需要传入一个SEL参数,用于指定调用哪个方法
makeObjectsPerformSelector:
- 依次调用NSArray集合中的每个元素的指定方法(带参数),该方法的第一个SEL参数用于指定调用哪一个方法,第二个参数用于调用集合元素的方法时传入的参数
makeObjectsPerformSelector:withObject:
//eg:
NSArray* array = @[[FKUser alloc] initWithName:@"sun" Withpass:@"123"],
[[FKUSer alloc] initWithName:@"jie" Withpass:@"234"],
[[FKUser alloc] initWithName:@"zhao" Withpass:@"456"];
[array makeObjectsPerformSelector:@selector(say:) withObject:@"good morning,i am handsome"];
如果希望对集合中的所有元素进行隐式遍历,并使用元素来执行某段代码,则可以通过NSArray的如下方法来完成
- 遍历集合中的所有元素,并且依次使用元素来指定代码块
enumerateObjectsUsingBlock:
- 遍历集合的所有元素,并依次使用元素来执行指定的代码块,该方法可以额外的传入一个参数,用于控制遍历的选项,如反向便遍历
enumerateObjectsWithOptions:usingBlock:
- 遍历集合中指定范围内的元素,并依次使用元素来执行指定的代码块,该方法可以额外传入一个参数,用于控制遍历的选项参数,用于控制遍历的选项,如反向遍历
enumerateObjectsAtIndexes:options:usingBlock:
示例:
@autoreleasepool {
NSString* s1 = @"laoda";
NSString* s2 = @"laoer";
NSString* s3 = @"laosan";
NSString* s4 = @"laoba";
NSArray* array = @[s1, s2, s3, s4];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@.length = %lu",obj,[obj length]);
NSLog(@"idx = %lu",idx);
if(idx == 2) {
*stop = 1;//改为yea那么就会停止遍历
}
}];
}
上面的三个方法都需要传入一个代码块参数,这个代码块必须带三个参数,第一个参数代表正在遍历的集合元素,第二个参数代表正在遍历的集合元素的索引,第三个参数就是用于遍历集合的元素代码
示例:
NSArray* array = @[[FKUser alloc] initWithName:@"sun" Withpass:@"123"],
[[FKUSer alloc] initWithName:@"jie" Withpass:@"234"],
[[FKUser alloc] initWithName:@"zhao" Withpass:@"456"],
[[FKUser alloc] initWithName:@"wang" Withpass:@"000"];
[array makeObjectsPerformSelector:@selector(say:) withObject:@"good morning"]
NSString* content = @"hello";
[array enumerateObjectsAtIndexes:
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 2)]
options:NSEnumerationReverse
usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"正在处理第%ld个元素:%@", idx, obj);
[obj say:content];
}];
对NSArray进行排序
typedef NS_ENUM(NSInteger, NSComparisonResult) {
NSOrderedAscending = -1, // 升序(左 < 右)
NSOrderedSame = 0, // 相同
NSOrderedDescending = 1 // 降序(左 > 右)
};
sortedArrayUsingFunction:context:
// 比较函数:按字符串长度排序
NSInteger stringLengthComparator(id str1, id str2, void *context) {
NSString *string1 = (NSString *)str1;
NSString *string2 = (NSString *)str2;
if ([string1 length] < [string2 length]) {
return NSOrderedAscending; // 升序(str1 在前)
} else if ([string1 length] > [string2 length]) {
return NSOrderedDescending; // 降序(str2 在前)
} else {
return NSOrderedSame; // 相等
}
}
// 使用示例
NSArray *originalArray = @[@"apple", @"cat", @"banana", @"dog"];
NSArray *sortedArray = [originalArray sortedArrayUsingFunction:stringLengthComparator context:nil];
NSLog(@"排序前: %@", originalArray);
NSLog(@"排序后: %@", sortedArray);
- 该方法使用排序函数对集合元素进行排序,该排序函数必须返回NSOrderedDescending、NSOrderedAscending、NSOrderedSame这些枚举值,用于代表集合的大小,该方法会返回一个排好序的NSArray对象
sortedArrayUsingSelector
//eg
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (NSComparisonResult)compareByAge:(Person *)other;
@end
@implementation Person
- (NSComparisonResult)compareByAge:(Person *)other {
return [@(self.age) compare:@(other.age)];
}
@end
// 使用示例
Person *p1 = [[Person alloc] init]; p1.age = 25;
Person *p2 = [[Person alloc] init]; p2.age = 20;
Person *p3 = [[Person alloc] init]; p3.age = 30;
NSArray *people = @[p1, p2, p3];
NSArray *sortedPeople = [people sortedArrayUsingSelector:@selector(compareByAge:)];
// 输出排序后的年龄
for (Person *person in sortedPeople) {
NSLog(@"年龄: %ld", (long)person.age);
}
- 该方法使用集合元素自身的方法对集合元素进行排序,集合元素必须返回NSOrderedDescending、NSOrderedAscending、NSOrderedSame这些枚举值,用于代表集合的大小,该方法会返回一个排好序的NSArray对象
sortedArrayUsingComparator:
- 该方法使用代码块对集合元素进行排序,该代码块必须返回NSOrderedDescending、NSOrderedAscending、NSOrderedSame这些枚举值,用于返回集合大小,同样返回一个排序好的新的NSArray对象
(后续学习block在进行补充)
componentsJoinedByString:
示例:
NSString* s1 = @"laoda";
NSString* s2 = @"laoer";
NSString* s3 = @"laosan";
NSString* s4 = @"laoba";
NSArray* array = @[s1, s2, s3, s4];
NSString* str = [array componentsJoinedByString:@"+"];
NSLog(@"%@",str);
//输出:laoda+laoer+laosan+laoba
- 用符号将数组中的元素(可以为空格)连接起来构成一个字符串
NSArray* arr = [str componentsSeparatedByString:@","];
- 将字符串用指定的符号分隔开来,并存储到NSArray数组中
使用枚举器遍历NSArray集合元素
对于NSArray对象,出了可以用集合元素的索引来遍历集合元素之外,还可以调用NSArray对象的如下两种方法来返回枚举器
objectEnumerator:
- 返回NSArray集合的顺序枚举器
reverseObjecctEnumerator:
- 返回NSArray的顺序枚举器
上面的两个方法都返回一个NSEumerator枚举器,该枚举器只包含如下两个方法
allObjects:
- 获取被枚举集合中的每一个元素
nextObject:
- 获取被枚举集合中的下一个元素
当遇到nil的时候遍历结束
使用NSEnumerator枚举器遍历集合元素示例:
#import<Foundation/Foundation.h>
int main(int argc, char* argv[]) {
@autoreleasepool {
//读取磁盘中的文件,用文件内容来进行初始化NSArray集合
NSArray* array = [NSArray arrayWithContentOfFile:@"myFile.txt"];
//获取NSArray的顺序枚举器
NSEnumerator* en = [NSArray objectEnumerator];
id object;
while (object = [en nextObject]) {
NSLog(@"%@",object);
}
NSLog(@“——————————————————下面逆序遍历——————————————————”);
//获取逆序枚举器
en = [array reverseObjectEnumerator];
while (object = [en nextObject]) {
NSLog(@"%@",object);
}
}
}
总结:
枚举器一开始指向第一个元素之前
- 先获取枚举器
- 定义一个变量储存第一个元素
- 进入循环,枚举下一个元素
快速枚举(for- in)
语法格式:
for (元素类型 变量名 in 数组名) {
NSLog(........);
//通过变量名来遍历数据
}
- 声明在for循环中的元素叫迭代变量
示例
laoer
laosan
laoba
Program ended with exit code: 0 @autoreleasepool {
NSString* s1 = @"laoda";
NSString* s2 = @"laoer";
NSString* s3 = @"laosan";
NSString* s4 = @"laoba";
NSArray* array = @[s1, s2, s3, s4];
for (NSString* temp in array) {
NSLog(@"%@",temp);
}
}
//输出
laoda
laoer
laosan
laoba
NSMutableArray
-
是NSArray的子类
-
仍然是一个数组,具备NSArray数组的特点,只能存储OC对象,每一个元素在内存中都是紧密相连的
-
相对于父类NSArray的拓展:
1. 可以进行动态的新增与删除 1. 其他用法与NSArray一致
-
创建:
NSMutableArray* arr1 = [NSMutableArray new];
NSMutableArray* arr2 = [[NSMutableArray alloc] init];
NSMutableArray* arr3 = [NSMutableArray array];
三种方法创建的数组的元素是0,有意义的,因为我们可以进行动态的增加与删除元素
NSMutableArray* arr4 = [NSMutableArray arrayWithObjects:@"laoda",@"laoer",@"laosan",@"laosi", nil];
这样创建的可变数组也可以进行新增与删除
不可以使用@[–]创建NSMutableArray数组
- 如何向可变数组中增加元素
addObject:
//将传入参数,作为可变数组参数传入
如果将另一个数组使用这个方法增加到被添加的数组中,那么原数组的长度加一,将传入的数据作为可变数组的一个元素
NSArray上存储的时id类型的指针,没有太严格的静态要求,并且其自身也是一个对象
addObjectsFromArray:
将传入数组中的每一个元素添加到被添加数组中
insertObject: atIndex:
将传入参数插入到指定下标
- 删除可变数组中指定下标的元素
removeObjectAtIndex:
- 删除可变数组中的指定元素
removeObject:
- 删除指定范围中的元素
removeObject: inRange:NSMakeRange(0,3)
- 删除最后一个元素
removeLastObject
- 删除所有元素
removeAllObjects:
将c类型数据转为OC类型
无论是NSArray还是NSMutableArray都只能存OC对象
先用一个OC对象把数据包装起来,然后把对象须存储到数组中
- 自定义包装类来包装基础数据类型
- 手动版
CZNumber* num1 = [[CZNumber alloc] initWithIntValue:10];
CZNumber* num2 = [[CZNumber alloc] initWithIntValue:20];
CZNumber* num3 = [[CZNumber alloc] initWithIntValue:30];
NSArray* arr = @[num1, num2, num3];
CZNumber* num = arr[0];
NSLog(@"%d",num.intValue);
NSLog(@"%d",((CZNumber*)arr[0]).intValue);
- 自动版
NSNumber是Foundation框架中定义好的一个类,这个类的对象的作用就是用来包装基础数据类型的
使用步骤:
- 先将基本数据类型包装到NSNumber对象中
- 再将NSNumber对象存储到NSArray数组中
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSNumber* num1 = [NSNumber numberWithInt:10];
NSNumber* num2 = [NSNumber numberWithInt:20];
NSNumber* num3 = [NSNumber numberWithInt:30];
NSNumber* num4 = [NSNumber numberWithInt:40];
NSArray* array = @[num1, num2, num3, num4];
for (NSNumber* num in array) {
NSLog(@"%d", num.intValue);
}
}
return 0;
}
//输出结果:
10
20
30
40
- 简写方式
NSArray* array = @[@10,@20,@30,@40];
@10:代表一个NSNumber对象,这个对象中包装的是整形的10
如果后面的数据是一个变量,需要用括号括起来
int num1 = 10;
int num2 = 20;
NSArray* arr = @[@(num1),@(num2)];