Foundation框架详解
字符串(NSString和NSMutableString)
NSString代表字符序列不可变的字符串,NSMutableString代表字符序列可变的字符串
创建字符串
NSString的功能非常强大,大致包括如下功能
- 创建字符串:创建字符串既可使用以init开头的实例方法,也可使用以string开头的类方法,当然也可以直接用@""的形式给出字符串直接量。
- 读取文件或网络URL来初始化字符串。
- 将字符串内容写入文件或URL。
- 获取字符串长度,既可获取字符串内包括的字符个数,也可获取字符串包括的字节个数。
获取字符串中的字符或字节,既可获取指定位置的字符,也可获取指定范围的字符。 - 获取字符串对应的C风格字符串。
- 连接字符串。
- 分隔字符串。
- 查找字符串内指定的字符和子串。
- 替换字符串。
- 比较字符串。
- 字符串大小比较。 ??与上面有什么区别
- 对字符串中的字符进行大小写转换。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
unichar data[6] = {97,98,99,100,101,102}; //unichar数组就是unsigned short的别名
NSString *str = [[NSString alloc] initWithCharacters:data length:6];
NSLog(@"%@",str);
char *cstr = "Hello, iOS!";
NSString *str2 = [NSString stringWithUTF8String:cstr];
NSLog(@"%@",str2);
[str2 writeToFile:@"myFile.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];//文件在哪????
NSString* str3 = [NSString stringWithContentsOfFile:@"NSStringTest.m" // 如何读入
encoding:NSUTF8StringEncoding error:nil];
NSLog(@"%@",str3);
}
}
上面简单示范了创建NSString对象的三种方式。
NSString的常用功能
以程序示例
#import <Foundation/Foundation.h>
#import "FKUser.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* str = @"我爱玩";
NSString* game = @"原神";
str = [str stringByAppendingString:@"原神!"];
NSLog(@"%@",str);
const char* cstr = [str UTF8String];
NSLog(@"获取的C字符串:%s",cstr);
str = [str stringByAppendingFormat:@"%@是一款3a巨作",game];
NSLog(@"%@" , str);
NSLog(@"str的字符个数为:%lu",[str length]);
NSLog(@"str按UTF-8字符集解码后字节数为:%lu",[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
NSString *s1 = [str substringToIndex:5];
NSLog(@"%@",s1);
NSString *s2 = [str substringFromIndex:6];
NSLog(@"%@",s2);
NSString *s3 = [str substringWithRange:NSMakeRange(6, 7)];
NSLog(@"%@",s3);
NSRange pos = [str rangeOfString:@"原神"];
NSLog(@"原神在str中出现的开始位置:%ld,长度为:%ld", pos.location,pos.length);
str = [str uppercaseString];
NSLog(@"%@",str);
}
}
对字符串个数和位置有一点疑惑。需要解决。
NSMakeRange
函数
声明如下:
objective
复制
NSRange NSMakeRange(NSUInteger location, NSUInteger length);
location
:表示范围的起始位置(索引),从 0 开始计数。length
:表示范围的长度,即包含的元素个数。
可变字符串
NSString是不可变类,一旦NSString对象被创建,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
NSMutableString对象则代表一个字符序列可变的字符串
改变序列的方法:
示例:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *game = @"永劫无间";
NSMutableString* str = [NSMutableString stringWithString:@"玩游戏"];
[str appendString:@"我只打劫。"];
NSLog(@"%@",str);
[str appendFormat:@"%@是世界上最好的游戏",game];
NSLog(@"%@",str);
[str insertString:@"哈哈" atIndex:0];
NSLog(@"%@",str);
[str deleteCharactersInRange:NSMakeRange(0, 10)];
NSLog(@"%@",str);
[str replaceCharactersInRange:NSMakeRange(0,5) withString:@"原神"];
NSLog(@"%@",str);//疑问,对字符串的位置和越界问题
}
}
NSMutableString类:可变字符串
NSMutableString
类是 Objective-C 中的一个可变字符串类,它是 NSString
类的子类。相对于 NSString
的不可变性,NSMutableString
具有以下作用和特点:
-
可变性(Mutability):
NSMutableString
对象是可变的,这意味着可以对其进行修改、添加和删除操作。你可以在已有字符串的基础上进行更改,而无需创建新的字符串对象。这种可变性使得NSMutableString
在需要频繁操作字符串的情况下更加高效。 -
追加和插入操作:
NSMutableString
提供了一系列方法用于在字符串末尾追加字符、字符串或格式化的文本。你可以使用appendString:
、appendFormat:
、insertString:atIndex:
等方法来实现字符串的追加和插入操作。appendString:
:appendString:
方法用于将指定的字符串追加到可变字符串的末尾。
NSMutableString *mutableString = [NSMutableString stringWithString:@"Hello"];//请注意,stringWithString:方法实际上是NSString类的一个便捷方法,等效于使用[[NSString alloc] initWithString:]来创建字符串的副本。
[mutableString appendString:@" World!"];
NSLog(@"%@", mutableString);
-
appendFormat:
:方法用于将格式化的文本追加到可变字符串的末尾。 -
insertString:atIndex:
:insertString:atIndex:
方法用于在可变字符串的指定位置插入指定的字符串。下图即插入索引5。
NSMutableString *mutableString = [NSMutableString stringWithString:@"Hello!"];
[mutableString insertString:@" World" atIndex:5];
NSLog(@"%@", mutableString); // Output: Hello World!
-
删除和替换操作:除了追加和插入操作,
NSMutableString
还提供了删除和替换字符串的方法。你可以使用deleteCharactersInRange:
、replaceOccurrencesOfString:withString:options:range:
等方法来删除或替换字符串中的特定部分。 -
可变长度:
NSMutableString
的长度是可变的。你可以在字符串中插入或删除字符,从而改变字符串的长度,而不会引起异常或错误。 -
与其他字符串类的转换:
NSMutableString
可以与其他字符串类进行相互转换。你可以通过mutableCopy
方法将不可变的NSString
对象转换为可变的NSMutableString
对象,反之亦然。
需要注意的是,由于 NSMutableString
是可变的,它并不适用于需要保持字符串不变的情况。如果你有一个字符串不需要修改,并且希望确保其内容不会被意外更改,应该使用不可变的 NSString
类。
日期与时间
NSdate
NSdate对象代表日期与时间,既可以通过类方法创建NSDate对象,也可以通过init实例方法来初始化NSDate对象。
下面是一些创建的实例:
#import <Foundation/Foundation.h>
#import "FKUser.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDate *date1 = [NSDate date];
NSLog(@"%@",date1);
NSDate *date2 = [[NSDate alloc] initWithTimeIntervalSinceNow:3600*24];
NSLog(@"%@",date2);
NSDate *date3 = [[NSDate alloc ] initWithTimeIntervalSinceNow: -365*3600*24*19];
NSLog(@"%@",date3);
NSDate* date4 = [NSDate dateWithTimeIntervalSince1970:3600 * 24 * 366 * 20];
NSLog(@"%@",date4);
NSLocale* cn = [NSLocale currentLocale];
NSLog(@"%@",[date1 descriptionWithLocale:cn]);
NSDate *earlier = [date1 earlierDate:date2];
NSDate *later = [date1 laterDate:date2];
switch ([date1 compare:date3]) {
case NSOrderedAscending:
NSLog(@"date1位于date3之前");
break;
case NSOrderedSame:
NSLog(@"date1与date3日期相等");
break;
case NSOrderedDescending:
NSLog(@"date1位于date3之后");
break;
}
NSLog(@"date1与date2之间时间差%g秒", [date1 timeIntervalSinceDate:date3]);
NSLog(@"date2与现在时间间差%g秒", [date2 timeIntervalSinceNow]);//%g自动选择%f或者%e,输出宽度较短的格式,且不输出无意义的0
}
}
compare方法与NSComparisonResult枚举类型
NSOrderedAscending
是NSComparisonResult
枚举类型的一个成员之一。NSComparisonResult
用于表示比较两个对象的结果,包括三个可能的取值:NSOrderedAscending
、NSOrderedSame
和NSOrderedDescending
。
NSOrderedAscending
表示第一个对象在排序中应该排在第二个对象之前。换句话说,第一个对象的值较小或者按照排序规则较早。
通常,用于比较的方法会返回NSComparisonResult
类型的值,以指示两个对象的比较结果。例如,当使用compare:
方法比较两个NSString
对象时,返回的值可能是NSOrderedAscending
、NSOrderedSame
或NSOrderedDescending
。
注意:NSOrderedAscending
、NSOrderedSame
或NSOrderedDescending
。为常量,是使用compare的返回值。
使用实例: 待深入了解
switch ([date1 compare:date3]) {
case NSOrderedAscending:
NSLog(@"date1位于date3之前");
break;
case NSOrderedSame:
NSLog(@"date1与date3日期相等");
break;
case NSOrderedDescending:
NSLog(@"date1位于date3之后");
break;
}
日期格式器
NSDateFormatter代表一个日期格式器,功能为完成NSDate与NSString之间的转换。步骤如下:
-
创建一个NSDateFormatter对象。
-
调用setDateStyle: ,setTimeStyle:方法设置格式化日期时间的风格。
-
NSDateFormatterNoStyle:不显示日期时间的风格
-
NSDateFormatterShortStyle:显示“短”的日期时间风格。
-
NSDateFormatterMediumStyle:显示“中”的日期时间风格。
-
NSDateFormatterLongStyle:显示“长”的日期时间风格。
-
NSDateFormatterFullStyle:显示“完整”的日期时间风格。
还可以调用NSdateFormatter和setDateFormate:方法设置自己的模板
-
-
如果要将NSDate转换为NSString,调用NSDateFormatter的string FromDate:方法执行格式化。
-
如果要将NSString转换为NSDate,调用NSDateFormatter的dateFromString:方法执行格式化。
#import <Foundation/Foundation.h>
#import "FKUser.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDate* dt = [NSDate dateWithTimeIntervalSince1970:3600*24*366*20];
NSLocale* locales[] = {[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"],
[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]};
NSDateFormatter* df[8];
for (int i = 0; i < 2; i++) {
df[i * 4] = [[NSDateFormatter alloc] init];
[df[i * 4] setDateStyle:NSDateFormatterShortStyle];
[df[i * 4] setTimeStyle:NSDateFormatterShortStyle];
[df[i * 4] setLocale:locales[i]];
df[i * 4 + 1] = [[NSDateFormatter alloc] init];
[df[i * 4 + 1] setDateStyle:NSDateFormatterMediumStyle];
[df[i * 4 + 1] setTimeStyle:NSDateFormatterMediumStyle];
[df[i * 4 + 1] setLocale:locales[i]];
df[i * 4 + 2] = [[NSDateFormatter alloc] init];
[df[i * 4 + 2] setDateStyle:NSDateFormatterLongStyle];
[df[i * 4 + 2] setTimeStyle:NSDateFormatterLongStyle];
[df[i * 4 + 2] setLocale:locales[i]];
df[i * 4 + 3] = [[NSDateFormatter alloc] init];
[df[i * 4 + 3] setDateStyle:NSDateFormatterFullStyle];
[df[i * 4 + 3] setTimeStyle:NSDateFormatterFullStyle];
[df[i * 4 + 3] setLocale:locales[i]];
}
for (int i = 0; i < 2 ;i++) {
switch (i)
{
case 0:
NSLog(@"------中国日期格式------");
break;
case 1:
NSLog(@"------美国日期格式------");
break;
}
NSLog(@"SHORT格式的日期格式:%@", [df[i * 4] stringFromDate:dt]);
NSLog(@"MEDIUM格式的日期格式:%@", [df[i * 4 + 1] stringFromDate:dt]);
NSLog(@"LONG格式的日期格式:%@", [df[i * 4 + 2] stringFromDate:dt]);
NSLog(@"FULL格式的日期格式:%@", [df[i * 4 + 3] stringFromDate:dt]);
NSString *dateStr = @"2024-01-01-01-50-50";
NSDateFormatter *df1 = [[NSDateFormatter alloc] init];
[df1 setDateFormat:@"yyyy-MM-dd-hh-mm-ss"];
NSDate* date2 = [df1 dateFromString: dateStr];
NSLog(@"%@",date2);//好神奇的输出
// NSString *dateStr = @"2024-01-01";
// NSDateFormatter *df1 = [[NSDateFormatter alloc] init];
// [df1 setDateFormat:@"yyyy-MM-dd"];
}
}
}
日历与日期组件
对象复制
copy与mutableCopy方法
两者都是复制一个对象的副本,copy方法返回对象的不可修改的副本。mutableCopy方法返回对象可修改的副本。
简而言之,返回的对象可不可以修改,与原本的字符串对象无关,与用哪个方法有关。
如程序调用NSString的mutableCopy方法,将会返回一个NSMutableString对象。
二者对返回原对象的副本,对复制的副本进行修改,原对象通常不会收到影响。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableString* heart = [NSMutableString stringWithString:@"cold"];
NSMutableString* heartCopy = [heart mutableCopy];
[heartCopy replaceCharactersInRange:NSMakeRange(0, 4) withString:@"hot"];
NSLog(@"heart是:%@",heart);
NSLog(@"heartCopy是:%@",heartCopy);
NSString *str = @"cold";
NSMutableString *strCopy = [str mutableCopy];
[strCopy appendString:@" become hot"];
NSLog(@"%@",strCopy);
NSMutableString *heart2 = [heart copy];
//[heart2 appendString:@"become hot"];
}
}
只要用mutableCopy复制的副本都可变。用Copy复制的副本不可变
NSCopying与NSMutableCopy协议
保证一个对象可调用copy方法来复制自身到不可变副本,通常需要做如下事情:
- 让该类实现NSCopying协议. (协议内容即下面的方法)
- 让该类实现copyWithZone:方法
保证一个对象可调用mutableCopy方法来复制自身到可变副本,通常需要做如下事情
- 让该类实现NSMutableCopying协议
- 让该类实现mutableCopyWithZone:方法
调用上述两种方法的返回实际上就是返回copyWithZone和mutableCopyWithZone的返回值。
示例:
//.h
#import <Foundation/Foundation.h>
@interface Dog : NSObject
@property (nonatomic, strong) NSMutableString* name;
@property (nonatomic, assign) int age;
@property (nonatomic, strong) NSString* variety;
@end
//.m
#import "Dog.h"
@implementation Dog
@synthesize name;
@synthesize age;
@synthesize variety;
- (id) copyWithZone:(NSZone*)zone
{
NSLog(@"--执行copyWithZone--");
Dog* dog = [[[self class] allocWithZone:zone] init];//self class的作用是获取当前对象所属的类,用于动态创建与当前对象类型相同的新对象。这样可以确保在继承关系中正确地创建副本,并保持类型的一致性。 !!!!!!!!!!!!!!
dog.name = self.name;
dog.age = self.age;
dog.variety = self.variety;
return dog;
}
@end
//
#import <Foundation/Foundation.h>
#import "Dog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog* dog1 = [[Dog alloc] init];
dog1.name = [NSMutableString stringWithString:@"毛毛"];
dog1.age = 8;
dog1.variety = @"VIP";
Dog* dog2 = [dog1 copy];
dog2.name = [NSMutableString stringWithString:@"多多"];
dog2.age = 7;
NSLog(@"dog1的名字为:%@",dog1.name);
NSLog(@"dog1的年龄为:%d",dog1.age);
NSLog(@"dog1的品种为:%@",dog1.variety);
NSLog(@"dog2的名字为:%@",dog2.name);
NSLog(@"dog2的年龄为:%d",dog2.age);
NSLog(@"dog2的品种为:%@",dog2.variety);
}
return 0;
}
上述程序即实现了NSCopying协议并实现了copyWithZone:方法。- (id) copyWithZone:(NSZone*)zone该方法的zone参数与不同的储存区有关,通常无需过多的关心该程序。
重写Zone方法时,父类已经实现了NSCopying协议,并重写过copyWithZone方法,那么子类重写时应该调用父类的copy方法复制从父类继承得到的成员变量,然后对子类中定义的成员变量进行赋值。
格式如下:
- (id)copyWithZone:(NSZone*)zone
{
id obj = [super copy];
//对子类定义的成员变量赋值
return obj;
}
深浅复制
int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog* dog1 = [[Dog alloc] init];
dog1.name = [NSMutableString stringWithString:@"毛毛"];
Dog* dog2 = [dog1 copy];
[dog2.name replaceCharactersInRange: NSMakeRange(0,2 ) withString:@"多多"];
NSLog(@"dog1的名字为:%@",dog1.name);
NSLog(@"dog2的名字为:%@",dog2.name);
}
return 0;
} // out: 多多。多多。
上面的程序调用dog1的copy方法复制了一个副本,并将副本赋给dog2变量。当接下来修改dog2.name属性值时,输出dog1/dog2两个对象多多name属性,编译运行发现两者都发生改变。都变成了多多。为什么呢?
这就是程序的浅复制。即未复制对象,而是复制指针地址。
当创建第一个Dog对象时,并使用dog1指针指向对象后的内存存储示意图:
查看copyWithZone方法的代码:
dog.name = self.name;
dog.age = self.age;
dog代表复制出来的对象。程序将被复制对象的name赋值给dog对name。但name只是一个指针变量,存放的只是字符串的地址,并不是字符串本身。发上了浅复制。如图所示
我是图图
浅复制:当对象的实例变量是指针变量是,如果程序只是复制该指针的地址,而不是真正复制指针所指向的对象,这种方式被称为浅复制。
深复制:不仅会复制对象本身,而且会递归复制每个指针类型的实例变量,直到两个对象没有任何公共部分。修改如下:
-(void)copyWithZone:(NSZone*)zone
{
NSLog(@"--执行copyWithZone--");
Dog* dog = [[[self class] allocWithZone:zone] init];
dog.name = [self.name mutableCopy];
dog.age = self.age;
return dog;
}
先讲name实例变量复制一份可变副本,再将该可变副本的值赋给新对象的name实例变量。保证二者没有任何公用部分,实现了深复制。
setter方法的复制选项
setter和getter方法可以使用copy指示符。就是指定当程序调用setter方法复制时,实际上是将传入参数的副本赋给程序的实例变量。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ttsetter : NSObject
@property (nonatomic , copy) NSMutableString* name;
@end
NS_ASSUME_NONNULL_END
#import "ttsetter.h"
@implementation ttsetter
@synthesize name;
@end
#import <Foundation/Foundation.h>
#import "Dog.h"
#import "FKSon.h"
#import "ttsetter.h";
int main(int argc, const char * argv[]) {
@autoreleasepool {
ttsetter* test1 = [ttsetter new];
test1.name = [NSMutableString stringWithString:@"超级3g"];
[test1.name appendString:@"超级西柚"];//失败。
}
return 0;
}
上述代码无法正确使用appendString方法的原因:setter方法的代码为copy
-(void) setName:(NSMutableString*) aname
{
name = [aname copy];
}
默认复制该对象的不可变副本,因此上述程序调用该参数的copy方法得到的是不可变副本。
oc集合概述
大致分为NSArray,NSSet,NSDictionary三类。
- NSArray:代表有序,可重复的集合
- NSSet:代表无序,不可重复的集合
- NSDictionary:代表具有映射关系的集合
为了保存数量不确定的数据,以及保存具有映射关系的数据(关联数组),oc提供了集合类。集合类主要负责保存其他数据,也称为容器类。oc的集合分别由NSArray,NSSet,NSDictionary这三个类簇代表,还有NSMUtableArray,NSMutableSet,NSMutableDictionary三个字子类。
集合类与数组不同,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的指针变量);而集合只能保存对象(实际上只是保存对象的指针变量。)
- NSSet集合类似于罐子,无法记住添加元素的顺序,因此NSSet里的元素不能重复,否则无法准确识别出这个元素。
- NSArry集合类似于数组,可以记住每次添加元素的顺序,只是NSMutableArray的长度可变。
- NSDictionary集合也像一个罐子也像一个罐子,只是里面的每项数据都由两个值组成。
访问:
- NSArray集合中的元素直接根据元素的索引访问
- NSDictionary集合中的元素可以根据每项元素的其中一个访问另一个。
- NSSet集合中的元素只能根据元素本身来访问。这也是不允许重复的原因
NSEnumerationOptions参数
NSEnumerationConcurrent:并发迭代。多个元素可以同时并发处理,可能会以更快的速度完成迭代,但是顺序不确定。
NSEnumerationReverse:反向迭代。按照逆序处理集合的元素。
NSEnumerationOrdered:有序迭代。保持迭代顺序与集合元素的顺序一致。在默认情况下,迭代是无序的。
数组(NSArray与NSMutableArray)
NSArray的功能和用法
可用类方法(array开头)或者实例方法(init)开头。常见方法介绍:
- array:创建一个不包含任何元素的空NSArray。
- arrayWithContentsOfFile: / initWIthContentsOfFile: :读取文件内容来创建NSArray
- arrayWithObject: / initWithObject: :创建只包含指定一个元素的NSArray
- arrayWithObjects: / initWithObjects: :创建包含指定的N个元素的NSArray
- 简化语法:@[元素1,元素2,元素3……]
- 查询集合元素在NSArray中的索引
- 根据索引值取出NSArray集合中的元素
- 对集合元素整体调用方法
- 对NSArray集合进行排序
- 取出NSArray集合中的部分集合组成新集合
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *array = [NSArray
arrayWithObjects:@"季莹莹",@"胡桃",@"哈迪",@"殷紫萍", nil];
NSLog(@"第一个元素:%@",[array objectAtIndex:0]);
NSLog(@"第一个元素:%@",[array firstObject]);
NSLog(@"索引为1的元素:%@",[array objectAtIndex:1]);
NSLog(@"最后的一个元素:%@",[array lastObject]);
NSArray *arr1 = [array objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 2)]];
NSLog(@"zhelizhelizheli%@",arr1);
NSLog(@"殷紫萍的位置为:%ld",[array indexOfObject:@"殷紫萍"]);
NSLog(@"殷紫萍在1-2的位置为:%ld",[array indexOfObject:@"殷紫萍" inRange:NSMakeRange(1, 1)]);
array = [array arrayByAddingObject:@"刘炼"];
array = [array arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"水万象",@"火万象", nil]];
for (int i = 0 ; i< array.count; i++){
NSLog(@"%@",array[i]);
}
NSArray* arr2 = [array subarrayWithRange:NSMakeRange(1, 3)];
[arr2 writeToFile:@"/Users/liuyuanfu/Desktop/笔记/未命名.txt " atomically:YES];
}
return 0;
}
上面是一段示例代码。注意array[i]的方法在IOS5.0系统以上才能使用。
NSArray判断集合包含元素的标准:只有某个集合元素与被查找元素通过isEqual:方法比较返回YES,即认为包含该方法。
对集合元素整体调用方法
简单调用有两种方法:
- makeObjectsPerformSelector::依次调用NSArray集合中每个元素的指定方法,该方法需要传入一个SEL参数,用于指定调用哪种方法。
[array makeObjectsPerformSelector:@selector(someMethod)];//格式如上,该方法不能带有参数。
该方法需要注意两点:
- 被调用的方法必须没有参数和返回值,因为
makeObjectsPerformSelector:
方法不会传递任何参数,并且不关心方法的返回值。 - 如果数组中的某个对象不响应指定的方法,即该对象的类没有实现指定的方法,会引发运行时错误。
- **makeObjectsperformSelector:withObjects:**依次调用NSArray集合中每个元素的指定方法,第一个SEL参数用于指定方法,第二个参数用于调用时传入参数,第三个参数用于控制是否终止迭代。
- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(nullable id)object1 withObject:(nullable id)object2;//方法定义
该方法为实例方法,所以需要传入参数。如下图所示:
NSArray *array = @[obj1, obj2, obj3];
[array makeObjectsPerformSelector:@selector(someMethod:withParam2:) withObject:param1 withObject:param2];
该方法会将param1和param2作为参数传入前面的方法。
使用该方法需要注意:
- 被调用的方法必须具有两个参数,并且可以没有返回值。
- 如果被调用的方法有返回值,makeObjectsPerformSelector:withObject:withObject:方法将忽略返回值。
- 被调用的方法必须是可响应的,即对象的类必须实现指定的方法,否则会引发运行时错误。
如果想要对集合所有元素进行隐式的遍历,并使用集合元素来执行某一段代码,则可通过如下方法解决:
- enumerateObjectsUsingBlock::遍历集合中的所有元素,并以此使用元素来执行指定代码块。
语法:
[array enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
// 执行代码块的操作,可以使用 object 来访问当前元素
}];
参数:
object:当前遍历到的元素。
index:当前元素的索引。
*stop:一个指针,用于控制是否终止遍历。在代码块内部设 *stop = YES可以提前结束遍历。
- enumerateObjectsWithOptions:usingBlock::该方法可以额外传入一个参数,用于控制遍历的选项,如反向遍历。
语法:
[collection enumerateObjectsWithOptions:(NSEnumerationOptions)options usingBlock:^(id object, NSUInteger index, BOOL *stop) {
// 执行代码块的操作,可以使用 object 来访问当前元素
}];
参数:除上个方法的三个参数外,还有options参数。
options:一个枚举类型的参数,用于控制遍历选项,如反向遍历(NSEnumerationReverse
)等。
- enumerateObjectsAtIndexes:options:usingBlock::遍历集合中指定范围内的元素,并依次使用元素来执行指定的代码块。可以额外传入一个选项参数,用于控制遍历的选项,如反向遍历。
语法:
[collection enumerateObjectsAtIndexes:(NSIndexSet *)indexes options:(NSEnumerationOptions)options usingBlock:^(id object, NSUInteger index, BOOL *stop) {
// 执行代码块的操作,可以使用 object 来访问当前元素
}];
参数:除了options外,还有一个indexes参数来实现遍历集合中指定范围内的元素。
indexes:一个 NSIndexSet
对象,指定要遍历的元素的索引集合。
options:一个枚举类型的参数,用于控制遍历选项,如反向遍历(NSEnumerationReverse
)等。
其他参数和示例与前两个方法类似。
上面即是所有方法的详解,现在来实操一下这些方法吧。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface lncoketest1 : NSObject
@property (nonatomic , copy) NSString *name;
@property (nonatomic , copy) NSString *weapon;
- (void) say;
- (void) say1:(NSString*) content;
-(id) initWithName:(NSString*) aName weapon:(NSString*)aweapon;
@end
NS_ASSUME_NONNULL_END
#import "lncoketest1.h"
@implementation lncoketest1
@synthesize name;
@synthesize weapon;
- (void) say
{
NSLog(@"%@喜欢%@",self.name,self.weapon);
}
-(id) initWithName:(NSString*) aname weapon:(NSString*)aweapon
{
if(self = [super init])
{
name = aname;
weapon = aweapon;
}
return self;
}
- (void) say1:(NSString*) content
{
NSLog(@"%@说:%@",self.name,content);
}
@end
#import "lncoketest1.h"
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *array = [NSArray arrayWithObjects:
[[lncoketest1 alloc] initWithName:@"殷紫萍" weapon:@"太刀"],
[[lncoketest1 alloc] initWithName:@"胡桃" weapon:@"长剑"],
[[lncoketest1 alloc] initWithName:@"妖刀姬" weapon:@"斩马刀"],
[[lncoketest1 alloc] initWithName:@"哈迪" weapon:@"扇子"],
[[lncoketest1 alloc] initWithName:@"季莹莹" weapon:@"棍子"],
nil];
[array makeObjectsPerformSelector:@selector(say)];
NSString *content = @"我想当天选";
//此处还可以使用:
//NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 4)];
//用indexes替换下面的[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2,2)] 使代码简洁。
[array enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2,2)] options:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL * stop) //隐式方法的使用。用options反向遍历。
{
[obj say1:content];
}];
}
return 0;
}
上述代码即展示了调用集合中所有元素整体的方法,通过NSIndexSet来控制只对集合中部分元素迭代调用指定的代码块,通过options反向调用等方法。
对NSArray进行排序
排序终于可以直接sort啦
- sortedArrayUsingFunction:context::该方法使用排序函数对集合元素进行排序,该排序函数必须返回NSOrderedDescending(降序),NSOrderedAscending(升序),NSOrderedSame(相等)这些枚举值,用于代表集合元素的大小,该方法返回一个排好序的新NSArray对象。
- sortedArrayUsingSelector::该方法使用集合元素自身的方法对集合元素排序,同样必须返回上个方法的三种枚举值用于代表集合元素的大小,返回一个排好序的新NSArray对象。
- sortedArrayUsingComparator::该方法使用代码块对集合元素进行排序,其他与上述方法相同。
#import <Foundation/Foundation.h>
//排序函数
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, const char * argv[]) {
@autoreleasepool {
NSArray *array1 = [NSArray arrayWithObjects:@"Objecetive-C",@"C",@"C++",@"Ruby",@"Perl",@"Python",nil];
array1 = [array1 sortedArrayUsingSelector:@selector(compare:)];//集合元素的compare:方法进行排序
NSLog(@"%@", array1);
NSArray *array2 = [NSArray arrayWithObjects:
[NSNumber numberWithInt:20],
[NSNumber numberWithInt:12],
[NSNumber numberWithInt:-8],
[NSNumber numberWithInt:50],
[NSNumber numberWithInt:19],nil];
array2 = [array2 sortedArrayUsingFunction:intSort context:nil];//使用intSort函数进行排序
NSLog(@"%@",array2);
NSArray *array3 = [array2 sortedArrayUsingComparator:^(id obj1, id obj2) {//使用代码块对元素集合进行排序
{
if ([obj1 intValue] > [obj2 intValue]) {
return NSOrderedDescending;
}
if ([obj1 intValue] < [obj2 intValue]) {
return NSOrderedAscending;
}
return NSOrderedSame;
}
}];
NSLog(@"%@",array3);
}
return 0;
}
使用枚举器遍历NSArray集合元素
除了根据集合元素的索引来遍历集合元素之外,还可以调用NSArray两个方法来返回枚举器
- objectEnumerator:返回NSArray集合的顺序枚举器
- reverseObjectEnumerator:返回NSArray集合的逆序枚举器
上面两个方法都返回一个NSEnumerator枚举器,该枚举器只包含如下两个方法。
- allObject:获取被枚举集合的所有元素
- nextObject:获取被枚举集合中的下一个元素
一般情况下接触nextObjcet方法即可完成对集合元素进行枚举:可采用循环不断获取nextObject方法的返回值,知道该方法的返回值为nil结束循环
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *array = [NSArray arrayWitid hContentsOfFile:
@"weijianweizhi"];
NSEnumerator *en = [array objectEnumerator];
id object;
while (object = [en nextobject]) {
NSLog(@"%@",object);
}
NSLog(@"-----逆序遍历-----");
en = [array reverseObjectEnumerator];
while (object = [en nextobject]) {
NSLog(@"%@",object);
}
}
return 0;
}
该代码实现了顺逆序两种方式遍历NSArray集合中的元素。
快速枚举(for…in)
使用快速枚举遍历集合元素时,无需获得集合的长度,也无需根据索引来访问集合元素。
for (type variableName in collection)
{
//variableName 自动迭代访问每个元素...
}
type是集合元素的类型,variableName是一个形参名,快速枚举将自动将集合元素依次赋给该变量。
示例:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *array = [NSArray arrayWithContentsOfFile:
@"wenjianweizhi"];
for(id object in array){
NSLog(@"%@", object);
}
}
return 0;
}
上述代码不需要数组长度也无须根据索引来访问数组元素。快速枚举的本质是一个foreach循环。
foreach循环:无需循环条件,无须循环迭代语句,这些部分由系统完成。foreach循环自动迭代数组的每个元素,当每个元素都被迭代一次后,foreach循环自动结束。
使用foreach循环遍历NSArray时,元素会按照其在数组中的顺序依次被访问。即按照元素在数组中的索引顺序进行遍历。
使用foreach循环遍历NSSet时,元素的访问顺序也是无序的,因为NSSet是无序的元素集合。
使用foreach循环遍历NSDictionary时,元素的访问顺序并不是固定的,因为NSDictionary是无序的键值对集合。元素的访问顺序可能会受到哈希算法、键的顺序等因素的影响。 (实际操作中是按照映射关系输出)
可变数组
NSArray代表集合元素不可变的集合,一旦创建成功,程序不能进行增删改。
tips:NSArray只是保存对象的指针,因此,NSArray只保证这些指针变量中的地址不能改变,但指针变量所指向的对象是可以改变的。
NSArray的子类:NSMutableArray可以实现增删改功能,方法如下:
- 添加集合元素的方法:add开头
- 删除集合元素的方法:remove开头
- 替换集合元素的方法:replace开头
- 插入元素的方法:insert开头
- 对集合本身排序的方法:sort开头
注意:NSArray的三个sort方法是返回一个新的,排好序的NSArray对象,对NSMutableArray一样适用,此处的则是对集合本身排序
#import <Foundatoin/Foundation.h>
NSString *NSCollectionToString(NSArray *array)
{
NSMutableString *result = [NSMutabelString stringWithstring:@"["];
for(id obj in array)
{
[result appendString:[obj description]];
[result appending:@","];
}
NSUinteger len = [result length];
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"]"];
return result;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray * array = [NSMutableArray arrayWithContentsOfFile:@"wenjianrujing"];
[array addObject:@"chaojue"];
NSLog(@"最后追加以个元素后:%@",NSCollectionToString(array));
[array addObjectsFromArray:@"a",@"b",nil];
NSLog(@"最后追加两个元素后:%@",NSCollectionToString(array));
[array insertObject:@"fengkuang",atIndex:2];
NSLog(@"在索引为2处插入一个元素后:%@",NSCollectionToString(array));
[array insertObjcets:[NSArray arrayWithObjects:@"ad",@"sf",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)];
NSLog(@"删除索引为2-5处的元素后:%@",NSCollectionToString(array));
[array replaceObjectsAtIndex:2 withObject:@"ads"];
NSLog(@"替换索引为2处的元素后:%@",NSCollectionTostring(arr));
}
return 0;
}
分别展示addXxx、removeXxx、replaceXxx、方法进行增删改
集合(NSset和NSMutableSet)
回顾:NSset类似于罐子,将所有元素扔进罐子中。集合里的多个对象之间没有明显顺序,且集合不允许包含相同元素。
如果试图把两个相同元素放在同一个NSSet集合中,则会保留一个元素。
NSSet的功能和用法
NSSet按照Hash算法来储存集合中的元素,因此具有很好的存取和查找性能。
与NSArray相比,最大的区别是元素没有索引。
二者的相同点
- 都可通过count方法获取集合元素的数量。
- 都可通过快速枚举来遍历集合元素。
- 都可通过objectEnumerator方法获取NSEnumerator枚举器对集合元素进行遍历。由于 NSSet集合本身就是无序的,因此,提供反向迭代器没有意义。
- 都提供了makeObjectsPerformSelector:、makeObjectsPerformSelector:withObject:方法对集合元素整体调用某个方法,以及enumerateObjectsUsingBlock:、enumerateObjectsWithOptions: usingBlock:对集合整体或部分元素迭代执行代码块。
- 都提供了valueForKey:和setValue:forKey:方法对集合元素整体进行KVC编程。
- 都提供了集合的所有元素和部分元素进行KVO编程的方法。
下面单独介绍NSSet集合
首先是创建,以set开头为类方法。
- setByAddingObject::向集合中添加一个新元素,返回添加元素后的新集合。
- setByAddingObjectsFromSet::使用NSSet集合向集合中添加多个新元素,返回添加元素后的新集合。
- setByAddingObjectsFromArray::使用NSArray集合向集合中添加多个新元素,返回添加元素后的新集合。
- allObjects:返回该集合中所有元素组成的NSArray。
- anyObject:返回该集合中的某个元素。该方法返回的元素是不确定的,但该方法并不保证随机返回集合元素
然后是对集合的访问,遍历,和筛选方法:
- containsObject::判断集合是否包含指定元素。
- member::判断该集合是否包含与该参数相等的元素,如果包含,则返回相等的元素:否则返回nil。
- objectsPassingTest::需要传入一个代码块对集合元素进行过滤,满足该代码块条件的集合元素被保留下来并组成一个新的NSSet集合作为返回值。
- objectsWithOptions:passingTest::与前一个方法的功能基本相似,只是可以额外地传入一个NSEnumerationOptions迭代选项参数。
NSSet判断集合元素重复的标准
当NSSet集合中存入一个元素后,NSSet会调用该对象的Hash方法来得到该对象的HashCode的值,然后根据HashCode的值决定该对象中底层Hash表中的位置。
如果HashCode相同,接下来就会通过isEqual:方法来判断两个元素是否相等,如果两个元素通过isEqual:方法比较返回NO,则认为不相等,存放在底层Hash表的同一位置,并形成链。如果它们通过isEqual:比较返回YES,则认为两元素相同,不添加。
简而言之,判断相同的标准为:
- 两个对象通过isEqual:方法比较返回YES
- 两个对象的hash方法返回值也相等。
重写Hash方法的基本规则:
- 程序运行过程中,同一个对象多次调用Hash应该返回相同的值
- 当两个对象通过isEqual:方法比较返回YES时,两个对象的Hash应返回相等的值
- 对象中作为isEqual:比较标准的实例变量,都应该用来计算HashCode值
步骤:
- 把对象内每个有意义的实例变量计算出一个int类型的HashCode值
- 用第一步计算出来的多个HashCode组合计算出一个HashCode值。为了防止直接相加偶尔相等,可以通过为各实例变量的HashCode值乘任意一个质数后再相加。
return [f1 hash] * 31 + [f2 hash];
有序集合(NSOrderedSet与NSMutableOrderedSet)
这两集合既有NSSet集合的特征,也具有NSArray类似的功能。
- 不允许元素重复,与NSSet集合相同
- 可以保持元素的添加顺序,且每个元素都有索引,可以根据索引来操作元素。与NSArray功能相似。
字典!(NSDictionary) 和 NSMutableDictionary
NSDictionary用于保存具有映射关系的数据,因此NSDictionary保存着两组值,一组用于保存NSDictionary里的key,一组用于保存NSDictionary里的value。
注意:key和value可以是任何引用类型的数据,Map的key不允许重复。
key和value之间存在单向的一对一关系,即通过key总能找到唯一、确定的value。
NSDictionary的功能和用法
NSDictionary超多超多的常见方法:
创建NSDictionary对象方法:
- dictionary:创建一个不包含任何key-value对的NSDictionary。
- dictionaryWithContentsOfFile:/initWithContentsOfFile::读取指定文件的内容,使用指定的文件内容来初始化NSDictionary。该文件通常是由NSDictionary输出生成的。
- dictionaryWithDictionary:/initWithDictionary::使用已有的NSDictionary包含的 key-value对来初始化NSDictionary 对象。
- dictionaryWithObject:forKey::使用单个key-value对来创建NSDictionary对象。
- dictionaryWithObjects:forKeys:/initWithObjects:forKeys::使用两个NSArray分别指定 key、value集合,可以创建包含多个key-value对的NSDictionary。
dictionaryWithObjectsAndKeys:/initWithObjectsAndKeys::调用该方法时,需要按 value1,key1,value2,key2,…,nil 的格式传入多个key-value对。 - 除此之外,还可使用如下简化语法来创建NSDictionary对象:@{keyl: valuel,key2: value2, …}
访问该集合所包含的key或者value方法:
-
count:该方法返回该NSDictionary所包含的key-value对的数量。
-
allKeys: 该方法返回该NSDictionary所包含的全部 key。
-
allKeysForObject::该方法返回指定value对应的全部 key。
-
allValues:该方法返回该NSDictionary所包含的全部value。
-
objectForKey::该方法获取该NSDictionary中指定key对应的value。
-
objectForKeyedSubscript::通过该方法的支持,允许NSDictionary通过下标法来获取指定key 对应的value。
-
valueForKey::该方法获取该NSDictionary中指定key对应的value。
-
keyEnumerator:该方法返回用于遍历该NSDictionary所有key的NSEnumerator 对象。
-
objectEnumerator:该方法返回用于遍历该NSDictionary所有value的NSEnumerator对象。
-
enumerateKeysAndObjectsUsingBlock::使用指定的代码块来迭代执行该集合中所有的 key-value 对。
-
enumerateKeysAndObjectsWithOptions:usingBlock::使用指定的代码块来迭代执行该集合中所有的key-value对。该方法可以传入一个额外的NSEnumerationOptions参数。
-
writeToFile:atomically::将该NSDictionary对象的数据写入指定文件。
够多吧,来看点重点
看之前,先定义一个print方法,用于便捷打印key-value对的情况。
print函数优化输出
#import <Foundation/Foundation.h>
@interface NSDictionary (print)
- (void) print;
@end
@import "NSDictionary + print.h"
@implementation NSDictionary (print)
- (void) print
{
NSMutableString *result = [NSMutableString stringWithString:@"{"];
for (id key in self)
{
[result appendString: [key description]];
[result appendString:@"="];
[result appendString: [self[key] description]];
[result appendString:@","];
}
NSUInteger len = [result length];
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"}"];
NSLog(@"%@",result);
}
@end
是不是很熟悉的输出方法,与前面的颇有异曲同工之处。
通过key获取value的方法有两种:
- 调用NSDictionary的objectForKey:方法
- 直接使用下标法
[dictionary objectForKey:key];
dictionary[key];//实际上是调用NSDictionary的objectForKeyedSubscript:方法进行访问
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[[FKUser alloc] initWithName:@"sun" pass:@"123"],@"one",
[[FKUser alloc] initWithName:@"bai" pass:@"345"],@"two",
[[FKUser alloc] initWithName:@"sun" pass:@"123"],@"three",
[[FKUser alloc] initWithName:@"tang" pass:@"178"],@"four",
[[FKUser alloc] initWithName:@"niu" pass:@"155"],@"five",nil];
[dict print];
NSLog(@"dict包含%ld个key-value对",[dict count]);
NSLog(@"dict的所有key为:%@",[dict allKeys]);
NSLog(@"<FKUser[name=sum,pass=123]>所对应的所有key为:%@",[dict allKeysForObject:[[FKUser alloc] initWithName:@"sun" pass:@"123"]]);
NSEnumerator *en = [dict objectEnumerator];
NSObject *value;
while (value = [en nextObject])
{
NSLog(@"%@",value);
}
[dict enumerateKeysAndObjectsUsingBlock:
^(id key, id value, BOOL *stop) { // ^是块的开始,整个是块。
NSLog(@"key的值为:%@",key);
[value say:@"一夜终极狼人杀好玩"];
}];
上述代码展示了调用NSDictionary的方法来访问key-value的关键代码,如获取key-value对的数量,以及所有的key和遍历它的所有value的枚举器。最后使用enumerateKeysAndObjectsUsingBlock:方法对所有的key-value对迭代执行指定的代码块。
对NSDictionary的key排序
返回排序完成后的所有key组成的NSArray,有如下方法:
- keysSortedByValueUsingSelector::根据NSDictionary的所有key指定方法的返回值对key排序;调用value的该方法必须返回NSOrderedDescending(降序),NSOrderedAscending(升序),NSOrderedSame(相等)三个枚举值之一。
- keysSortedByvalueUsingComparator:该方法使用指定的代码块来遍历key-value对,并根据执行结果(必须返回如上三个枚举值之一)对NSDictionary的所有key进行排序
- keysSortedByValueWithOptions:usingComparator::与前一个功能相似,只是可以传入一个额外的NSEnumerationOptions参数。
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"Objective-c",@"one",
@"RUby",@"two",
@"Python",@"three",
@"Perl",@"four",nil];
[dict print];
NSArray *keyArr1 = [dict keysSortedByValueUsingSelector:
@selector(compare:)];
NSLog(@"@",keyArr1);
NSArray *keyArr2 = [dict keysSortedByValueUSingComparator:
^ (id value1, id value2)
{
if([value1 length] > [value2 length])
{
return NSOrderedDescending;
}
if([value1 length] > [value2 length])
{
return NSOrderedAscending;
}
return NSOrederSame;
}];
NSLog(@"%@", keyArr2);
[dict writeToFile:@"wenjianrujing" atomically:YES];
}
return 0;
}
上述代码分别才用两种方式对NSDictionary的所有key进行排序,其中一个方法是调用compare:方法进行排序----字符串比较大小直接根据字符对应的编码进行。第二段代码则通过调用代码块进行排序,代码块中的规则是根据字符串长度进行排序。
对NSDictionary 的key进行过滤
可以通过如下两个方法对NSDictionary的所有key进行过滤,这些方法执行完成后饭后满足过滤条件的key组成的NSSet。
- keysOfEntriesPassingTest::使用代码块迭代处理每个kye-value对。该代码必须返回BOOL类型的值,返回YES该key才会被保留。该代码接受三个参数:第一个参数代表正在处理迭代的key,第二个参数代表正在迭代处理的value,第三个参数代表是否还需要继续迭代,如果将第三个参数设置为NO,该方法会立刻停止迭代
- (NSSet<KeyType> *)keysOfEntriesPassingTest:(BOOL (^)(KeyType key, ObjectType obj, BOOL *stop))predicate;
上述代码为方法定义
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWihtInt:100], @"Objective-C",
[NSNumber numberWihtInt:50], @"Ruby",
[NSNumber numberWihtInt:60], @"Python",
[NSNumber numberWihtInt:70], @"Perl",nil];
[dict print];
NSSet *keySet = [dict keysOfEntriesPassingTest:
^ (id key, id value, BOOL* stop)
{
return (BOOL)([value intValue] > 80);
}];
NSLog(@"%@",keySet);
}
return 0;
}
- keysOfEntriesWithOptions:passingTest::基本与上述相似,但可以额外传入一个附加的NSEnumerationOptions参数
- (NSSet<KeyType> *)keysOfEntriesWithOptions:(NSEnumerationOptions)opts passingTest:(BOOL (^)(KeyType key, ObjectType obj, BOOL *stop))predicate;
上图的为方法定义。可以传入NSEnumerationOptions参数,实现并发或者反向枚举。
NSEnumerationConcurrent
: 并发枚举。允许在多线程环境中并发地遍历集合,提高性能。NSEnumerationReverse
: 反向枚举。按照与正常顺序相反的顺序遍历集合。
NSDictionary *dict = @{@"A": @1, @"B": @2, @"C": @3, @"D": @4, @"E": @5};
NSSet *keys = [dict keysOfEntriesWithOptions:NSEnumerationReverse passingTest:^ BOOL(id key, id obj, BOOL *stop) {
return [obj intValue] % 2 == 0; // 返回值为偶数的键,但是按照反向顺序遍历
NSLog(@"满足条件的键为:%@", keys);
}];
使用自定义类作为NSDictionary的key
前面的例子都是使用NSSring作为NSDictionary的key,但是我们可以用自定义类作为NSDictionary的key,需满足如下要求
- 该自定义类正确重写过isEqual:和Hash方法。要求为:当两个对象通过isEqual:方法判断相同时,两个对象的Hash方法返回值也相同。
- 该自定义类必须实现了copyWithZone:方法,该方法最好返回对象不可变副本。使用不可变副本是为了防止key索引被破坏,从而导致NSDictionary的完整性被破魂
- (id) copyWithZone:(NSZone *)zone
{
NSLog(@"---正在copy---");
FKUser* newUSer = [[[self class] allocWithZone:zone] init];
newUser->name = name;
newUser->pass = passs;
return newUser;
}
#import "NSDictionary+print.h"
#import "FkUser.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
FKUser *u1 = [[FKUser alloc] initWithName:@"bai" pass:@"354"];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"one", [[FKUser alloc] initWithName:@"sun" pass:@"123"],
@"two",u1,
@"three", [[FKUser alloc] initWithName:@"sun" pass:@"123"],
@"four", [[FKUser alloc] initWithName:@"tang" pass:@"178"],
@"five", [[FKUser alloc] initWithName:@"niu" pass:@"155"],nil];
[dict print];
}
return 0;
}
运行结果:
NSMutableDictionary的功能和用法
开始变啦,与NSArray与NSMutableArray相似,NSMutableDictionary增加了增删改的方法。
- setObject:forKey::设置一个key-value对。如果NSDictionary中没有包含与该key相同的 key-value 对,那么NSDictionary将会新增一个key-value对;否则该key-value对将会覆盖已有的key-value对。
- setObject:forKeyedSubscript::通过该方法的支持,允许程序通过下标法来设置key-value对。
- addEntriesFromDictionary::将另一个NSDictionary中所有的key-value对复制到当前 NSDictionary 中。
- setDictionary::用另一个NSDictionary中所有的key-value对替换当前NSDictionary 中的 key-value 对。
- removeObjectForKey::根据key来删除 key-value 对。 removeAllObjects:清空该NSDictionary。
- removeObjectsForKeys::使用多个key组成的NSArray作为参数,同时删除多个key对应的key-value对。
#import <Foundation/Foundation.h>
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:89],@"疯狂Android讲义", nil];
dict[@"疯狂Android讲义"] = [NSNumber numberWithInt:99];
[dict print];
NSLog(@"--再次添加key-value对--");
dict[@"疯狂iOS讲义"] = [NSNumber numberWithInt:69];
[dict print];
NSDictionary *dict2 = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:79],@"疯狂Ajax讲义",[NSNumber numberWithInt:89], @"Struts 2.x权威指南", nil];
[dict addEntriesFromDictionary:dict2];
[dict print];
[dict removeObjectForKey:@"Struts 2.x权威指南"];
[dict print];
}
return 0;
}
上述代码简单展示了常用的方法来动态改变NSMutableDictionary中的key-value对。