疯狂IOS讲义,字典
NSDictionary用于保存具有映射关系的数据,因此,NSDictionary集合里保存着两组值,一组值用于保存NSDictionary里的key,另一组值用于保存NSDictionary里的value。注意key和value都可以时任何指针类型的数据,NSDiction的key不允许重复。
key与value之间存在单项一对一关系,即通过指定的key,总能找到唯一的、确定的value。从NSDictionary中取出数据时,只要给出指定的key,就可以取出对应的value。如果把NSDictionary的两组值拆开来看,NSDictionary里的数据有如图7.7所示的结构。
从图7.7可以看出,如果把NSDictionary里的所有key放在一起,它们就组成一个NSSet集合(所有的key没有顺序,key和key之间不能重复),实际上,NSDictionary确实包含了一个allKeys方法,用于返回NSDictionary所有key组成的集合。遗憾的是,NSDictionary把allKeys方法的返回值身为NSArray——这说明该方法经过了进一步转换,它已经把NSSet集合转换成了NSArray集合。
NSDictionary的功能和用法
NSDictionary集合由多个key-value对组成,因此创建NSDictionary时需要同时指定多个key-value对。NSDictionary分别提供里类方法和实例方法来创建NSDictionary,两种创建方式需要传入的参数基本类似,只是以类方法dictionary开头,而实例方法则以init开头。下面是创建NSDictionary对象的积累常见方法。
一旦得到NSDictioanry对象接下来就可以通过方法来访问该集合所包含的key和value。NSDictionary提供了如下常用的方法。
下面程序为了能直接看到NSDictionary中包含的key-value对的详情,为NSDictionary扩展了一个print类别,在该类别中为NSDictionary扩展了一个print方法,用于打印NSDictionary种key-value的详情。该类别的接口部分代码如下。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSDictionary (print)
- (void) print;
@end
NS_ASSUME_NONNULL_END
接下来在该类的实现部分为print方法提供实现,NSDictionary的print实现部分代码如下:
#import "NSDictionary+print.h"
@implementation NSDictionary (print)
- (void) print {
NSMutableString* result = [NSMutableString
stringWithString:@"["];
//使用快速枚举法来说遍历NSDictionary
//循环计数器将依次等于该NSDictionary的每个key
for(id key in self) {
[result appendString:[key description]];
[result appendString:@"="];
//使用下标法根据key来获取对应的value
[result appendString:[self[key]
description]];
[result appendString:@", "];
}
//获取字符串长度
NSUInteger len = [result length];
//去掉字符串的最后两个字符
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"]"];
NSLog(@"%@", result);
}
@end
上面程序示范了NSDictionary的两个基本用法,程序可以使用快速枚举来遍历NSDictionary的所有key。除此之外,程序也可根据key来获取NSDictionary中对应的value。通过key来获取value有如下两种语法:
- 调用NSDictionary的objectForKey:方法即可根据key来获取对应的value。
- 直接使用下标法根据key来获取对应的value。
也就是说,下面两行代码的功能时相同的:
[dictionary objectForKey:key];
dictionary[key];
很明显,后一种表示方法更加简单、易用。但这个语法只能在iOS5.0以上的系统中使用,当程序调用dictionary[key]下表形式来获取value时,实际上就是调用NSDictioanry的objectForKeyedSubscript:方法进行访问。
下面的程序示范了NSDictionary的功能与用法。
#import<Foundation/Foundation.h>
#import"NSDictionary+print.h"
#import"FKUser.h"
//定义一个函数,该函数用于把集合转变为字符串
NSString* NSCollectionToString(id collection) {
NSMutableString* result = [NSMutableString
stringWithString:@"["];
//快速枚举遍历NSSet或NSArray集合
for(id obj in collection) {
[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 {
//直接使用多个value-key对的形式创建NSDictionary对象
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"]]);
//获取遍历dict所有value的枚举器
NSEnumerator* en = [dict objectEnumerator];
id value;
//使用枚举器来遍历dict中所有的value
while(value = [en nextObject]) {
NSLog(@"%@", value);
}
//使用指定代码块来迭代执行该集合中所有的key-value对
[dict enumerateKeysAndObjectsUsingBlock:
//该集合包含多少个key-value对,下面的代码块就执行相应的次数
^(id key, id value, BOOL *stop) {
NSLog(@"key的值为:%@", key);
[value say:@"疯狂iOS讲义"];
}
];
}
return 0;
}
编译、运行该程序,可以看到如下输出:
2022-06-07 20:05:52.452107+0800 练习[24835:5261663] [one=<FKUser[name=sun,pass=123]>, five=<FKUser[name=niu,pass=155]>, three=<FKUser[name=sun,pass=123]>, two=<FKUser[name=bai,pass=345]>, four=<FKUser[name=tang,pass=178]>]
2022-06-07 20:05:52.452557+0800 练习[24835:5261663] dict包含5个key-value对
2022-06-07 20:05:52.452608+0800 练习[24835:5261663] dict的所有key为:(
one,
five,
three,
two,
four
)
2022-06-07 20:05:52.452640+0800 练习[24835:5261663] <FKUser[name=sum,pass=123]>对应的所有key为:(
one,
three
)
2022-06-07 20:05:52.452674+0800 练习[24835:5261663] <FKUser[name=sun,pass=123]>
2022-06-07 20:05:52.452691+0800 练习[24835:5261663] <FKUser[name=niu,pass=155]>
2022-06-07 20:05:52.452703+0800 练习[24835:5261663] <FKUser[name=sun,pass=123]>
2022-06-07 20:05:52.452715+0800 练习[24835:5261663] <FKUser[name=bai,pass=345]>
2022-06-07 20:05:52.452726+0800 练习[24835:5261663] <FKUser[name=tang,pass=178]>
2022-06-07 20:05:52.452747+0800 练习[24835:5261663] key的值为:one
2022-06-07 20:05:52.452760+0800 练习[24835:5261663] sun说:疯狂iOS讲义
2022-06-07 20:05:52.452774+0800 练习[24835:5261663] key的值为:five
2022-06-07 20:05:52.452785+0800 练习[24835:5261663] niu说:疯狂iOS讲义
2022-06-07 20:05:52.452809+0800 练习[24835:5261663] key的值为:three
2022-06-07 20:05:52.481643+0800 练习[24835:5261663] sun说:疯狂iOS讲义
2022-06-07 20:05:52.481664+0800 练习[24835:5261663] key的值为:two
2022-06-07 20:05:52.481679+0800 练习[24835:5261663] bai说:疯狂iOS讲义
2022-06-07 20:05:52.481692+0800 练习[24835:5261663] key的值为:four
2022-06-07 20:05:52.481705+0800 练习[24835:5261663] tang说:疯狂iOS讲义
Program ended with exit code: 0
对NSDictionary的key排序
NSDictionary还提供了方法对NSDictionary的所有key执行排序,这些方法执行完成后将返回排序好的所有key组成的NSArray。NSDictionary提供的排序方法如下。
下面程序将示范分别使用两种方式对NSDictionary的key进行排序。
#import<Foundation/Foundation.h>
#import"NSDictionary+print.h"
#import"FKUser.h"
/*
//定义一个函数,该函数用于把集合转变为字符串
NSString* NSCollectionToString(id collection) {
NSMutableString* result = [NSMutableString
stringWithString:@"["];
//快速枚举遍历NSSet或NSArray集合
for(id obj in collection) {
[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 {
//使用简化语法创建NSDictionary对象
NSDictionary* dict = [NSDictionary
dictionaryWithObjectsAndKeys:
@"Objective-C" , @"one",
@"Ruby" , @"two",
@"Python" , @"three",
@"Perl" , @"four", nil];
//打印dict集合的所有元素
[dict print];
//获取所有直接调用value的compare:方法对所有的key进行排序
//返回排序好的所有key组成的NSArray
NSArray* keyArr1 = [dict keysSortedByValueUsingSelector:
@selector(compare:)];
NSLog(@"%@", keyArr1);
NSArray* keyArr2 = [dict keysSortedByValueUsingComparator:
//对NSDictionary的value进行比较,字符串越长,即可认为value越大
^(id value1, id value2) {
//下面定义大小比较的标准:字符串越长,即可认为value越大
if([value1 length] > [value2 length]) {
return NSOrderedDescending;
}
if([value1 length] < [value2 length]) {
return NSOrderedAscending;
}
return NSOrderedSame;
}];
NSLog(@"%@" , keyArr2);
//将NSDictionary的内容输出到指定文件中
[dict writeToFile:@"mydict.txt" atomically:YES];
}
return 0;
}
上面程序中前两段粗体字代码分别使用两种方式对NSDictionary的所有key进行排序,其中,第一段粗体字代码直接调用所有value的compare:方法进行排序——字符串比较大小直接根据字符对应的编码进行。对于程序中的4个value,它们的大小依次是Objective-C、Perl、Python、Ruby:第二段粗体字代码则调用代码块对NSDIctionary的所有value比较大小,代码块中的比较原则是:value对应的字符串越长,系统认为该value越大。按照这种规则,上面4个value的大小依次是:Ruby、Perl、Python、Objective-C。
编译、运行该程序,即可看到如下输出:
2022-06-08 16:55:14.820756+0800 练习[36871:5669777] [one=Objective-C, three=Python, two=Ruby, four=Perl]
2022-06-08 16:55:14.820983+0800 练习[36871:5669777] (
one,
four,
three,
two
)
2022-06-08 16:55:14.821020+0800 练习[36871:5669777] (
two,
four,
three,
one
)
Program ended with exit code: 0
上面程序输出的value的顺序正好与前面介绍的对value排序的结构相同。
程序的最后一行将NSDictionary的内容写入myDict.txt文件,该文件将会保存该NSDictionary中包含的所有key-value对。打开该文件,将会看到如下内容:
对NSdictionary的key进行过滤
NSDictionary还提供了方法对NSDictionary的所有key进行过滤,这些方法执行完成后将返回满足过滤条件的key组成的NSSet。NSDictionary提供了如下过滤方法。
如下程序示范了对NSDictionary进行过滤。
#import<Foundation/Foundation.h>
#import"NSDictionary+print.h"
int main(int argc , char * argv[]) {
@autoreleasepool {
//使用简化语法创建NSDictionary对象
NSDictionary* dict = @{
@"Objective-C": [NSNumber numberWithInt:89],
@"Ruby": [NSNumber numberWithInt:69],
@"Python": [NSNumber numberWithInt:75],
@"Perl": [NSNumber numberWithInt:109]
};
//打印dict集合的所有元素
[dict print];
//对NSDictionary的所有key进行过滤
NSSet* keySet = [dict keysOfEntriesPassingTest:
//使用代码块对NSDictionary的key-value对进行过滤
^(id key, id value, BOOL* stop) {
//当value的值大于80时返回YES
//这意味着只有value的值大于80的key才会被保存下来
return (BOOL)([value intValue] > 80);
}];
NSLog(@"%@", keySet);
}
return 0;
}
上面程序使用代码块对NSDictionary的key-value对进行迭代处理,代码块的判断标准是,只有当value的值大于80时才会返回YES,这表明只有当value的值大于80时才会保留对应的key。编译、运行该程序,可以看到如下输出:
上面程序保留了两个key:Perl和Objective-C,它们对应的value值分别为109和89,都大于80,因此可以被保留下来。
使用自定义类作为NSDictionary的key
前面介绍NSDictionary的功能和用法时,都是使用NSString作为NSDictionary的key。如果程序打算使用自定义类作为NSDictionary的key,则该自定义类必须满足如下要求:
可能有读者会感到奇怪,NSDictionary为何要求自定义的key类必须实现copyWithZone:方法(最好能让该类实现NSCopying协议)呢?这是因为NSDictionary安全性的考虑。当程序把多个key-value对放入NSDictionary集合之后,对于NSDictionary而言,key是非常关键的,NSDictionary需要根据key来访问value——从这个意义上看,**key相当于NSDictionary元素的索引。**如果key本身是可变的,且程序可以通过其他变量来修改NSDictionary的key,这就可能导致NSDictionary的“索引”值被破坏,从而导致NSDictionary的完整性被破坏。
为了避免上面所描述的情况出现,NSDictionary采用了更安全的做法,只要程序把任何对象都作为key放入NSDictionary中,NSDictionary总会先调用key的copy方法来复制该对象的不可变副本,然后使用该副本作为NSDictionary的key。
为了让前面的FKUser类可作为NSDictionary的key,还需要让FKUser类实现NSCopying协议(这是可选的,通常建议实现协议),并让该FKUser类实现copyWithZone:方法。FKUser实现的copyWithZone:方法如下。
程序清单:codes/07/7.8/FKUser.m
- (id) copyWithZone:(NSZone*)zone {
NSLog(@"--正在复制--");
//复制一个对象
FKUser* newUser = [[[self class] allocWithZone: zone] init];
//将被复制对象的实例变量的值赋给新对象的实例变量
newUser -> name = name;
newUser -> pass = pass;
return newUser;
}
接下来使用FKUser对象作为NSDictionary的key,程序如下。
程序清单:codes/07/7.8/NSDictionaryTest2.m
#import<Foundation/Foundation.h>
#import"NSDictionary+print.h"
#import"FKUser.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
FKUser* u1 = [[FKUser alloc] initWithName:@"bai
pass:@"345"];
//使用简化语法创建NSDictionary对象
NSDictionary* dict = @{
[[FKUser alloc] initWithName:@"sun"
pass: @"123"]: @"one",
u1: @"two",
[[FKUser alloc] initWithName: @"sun"
pass: @"123" :@"three",
[[FKUser alloc] initWithName: @"tang"
pass: @"178" :@"four",
[[FKUser alloc] initWithName: @"niu"
pass: @"155" :@"five",
//将u1的密码设为nil。
u1.pass = nil;
//由于NSDictionary并未直接使用u1所指向的FKUser作为key
//而是先复制了u1所指向对象的副本,然后以该副本作为key
//因此程序将可以看到dict的key不会受到任何影响
[dict print];
}
}
上面程序同样直接按value1,key1,value2,key2…的格式定义了NSDictionary的多个key-value对,只是此时该程序使用FKUser对象作为key。从前面的介绍可以知道,当程序尝试使用任何对象作为key时,会先调用copy方法来复制key的不可变副本,实际是以该副本作为NSDictionary的key。因此,上面程序对u1的pass进行修改时,NSDictionary的索引key并不会受到任何影响。
从上面的输出结果可以看到,程序依次调用了4次copyWithZone:方法来复制FKUser的副本,这是因为NSDictionary中只包含了4个key-value对。虽然程序指定了5个key-value对,但其中有两个key(FKUser对象)的name、pass完全相同,这两个对象通过isEqual:方法比较会返回YES,并且它们的hash方法返回值也相等,因此NSDictionary会认为这两个key是重复的,就会只保留一个。
NSMutableDictionary的功能与用法
NSMutableDictionary继承了NSDictionary,它代表一个key-value对可变的NSDictionary集合。由于NSMutableDictionary可以动态的添加key-valie对,因此,创建NSMutableDictionary集合时可指定初始容量。
类似于NSMutableArray与NSArray的关系,NSMutableDictionary主要在NSDictionary基础上增加了添加key-value对,删除key-value对的方法。
NSMutableDictionary主要新增了如下方法。
如下程序示范了使用方法来动态改变NSMutableDictionary中的key-value对。
程序清单:codes/07/7.8/NSMutableDictionaryTest.m
#import<Foundation/Foundation.h>
#import"NSDictionary+print.h"
int main(int argc , char * argv[]) {
@autoreleasepool {
//使用单个key-value对来创建NSMutableDictionary对象
NSMutableDictionary* dict = [NSMutableDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:89], @"疯狂Andriod", nil];
//使用下标法设置key-value对。由于NSDictionary中已存在该key
//因此此处设置的value会覆盖前面的value
dict[@"疯狂iOS讲义"] = [NSNumber numberWithInt:99];
[dict print];
NSLog(@"--再次添加key-value对--");
dict[@"疯狂XML讲义"] = [NSNumber numberWithInt:69];
[dict print];
NSDictionary* dict2 = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:79] , @"疯狂Ajax讲义",
[NSNumber numberWithInt:79] , @"疯狂Swift讲义"
, nil];
//将另一个NSDictionary中的key-value对添加到当前NSDictionary中
[dict addEntriesFromDictionary:dict2];
[dict print];
//根据key来删除key-value对
[dict removeObjectForKey:@"疯狂Swift讲义"];
[dict print];
}
return 0;
}
上面程序中的粗体字代码就是使用不同的方式为NSMutableDictionary动态增加、删除key-value对的代码。编译、运行该程序,即可看到如下输出:
2022-06-08 20:57:45.458301+0800 练习[39385:5774017] [疯狂iOS讲义=99, 疯狂Andriod=89]
2022-06-08 20:57:45.458506+0800 练习[39385:5774017] --再次添加key-value对–
2022-06-08 20:57:45.458549+0800 练习[39385:5774017] [疯狂iOS讲义=99, 疯狂Andriod=89, 疯狂XML讲义=69]
2022-06-08 20:57:45.458590+0800 练习[39385:5774017] [疯狂Ajax讲义=79, 疯狂Swift讲义=79, 疯狂Andriod=89, 疯狂iOS讲义=99, 疯狂XML讲义=69]
2022-06-08 20:57:45.458610+0800 练习[39385:5774017] [疯狂Ajax讲义=79, 疯狂Andriod=89, 疯狂iOS讲义=99, 疯狂XML讲义=69]
Program ended with exit code: 0
结合上面程序中粗体字代码对NSMutableDictionary中key-value对的改变,以及上面的输出结果,即可非常容易的体会到NSMutableDictionary中key-value对的增加、删除操作。