字典
1. NSDictionary的功能与用法
我们OC中提供了类方法与实例方法来创造我们的字典,两种方法传入的参数基本类似,只是类方法以dictionary开始,实例方法以init开头。
下面是创建NSDictionary的方法:
得到对象后就可以通过下列方法来访问集合中key和value:
下面将会示范NSDictionary的两个基本用法,程序可以通过快速枚举来遍历NSDictionary的所有key。除此之外,程序也可以根据key来获取NSDictionary中对应的value。
通过key来获取value有如下两种语法:
1.调用NSDictionary的objectForKey来获取相对应的value;
2.直接使用下标法根据key来获取对应的value;
[dictionary objectForKey: key];
dictionary[key];
接口部分:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSDictionary (print)
- (void) print;
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface FKUser : NSObject
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* pass;
- (id) initWithName: (NSString* ) name pass: (NSString* ) pass;
- (void) say: (NSString* ) content;
@end
NS_ASSUME_NONNULL_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
#import "FKUser.h"
@implementation FKUser
@synthesize name;
@synthesize pass;
- (id)initWithName:(nonnull NSString *) aname pass:(nonnull NSString *) apass {
if (self == [super init]) {
self.name = aname;
self.pass = apass;
}
return self;
}
- (void)say:(nonnull 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];
}
//重写Hash方法,从写该方法的标准为如果两个FKUser的name,pass相等,两个FKUser的Hash方法返回值相等
- (NSUInteger) hash {
NSLog(@"===hash===");
NSUInteger nameHash = name == nil ? 0 : [name hash];
NSUInteger passHash = pass == nil ? 0 : [pass hash];
return nameHash*31 + passHash;
}
@end
主函数:
#import <Foundation/Foundation.h>
#import "FKUser.h"
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//直接使用多个value,key的形式创建NSDictionary对象
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
[[FKUser alloc] initWithName:@"lai" pass:@"123"], @"one",
[[FKUser alloc] initWithName:@"gao" pass:@"345"], @"two",
[[FKUser alloc] initWithName:@"zhu" pass:@"123"], @"three",
[[FKUser alloc] initWithName:@"cui" pass:@"178"], @"four",
[[FKUser alloc] initWithName:@"li" pass:@"155"], @"five",
nil];
[dict print];
NSLog(@"dict包含%ld个key—value对", [dict count]);
NSLog(@"dict中所有的的key为:%@", [dict allKeys]);
NSLog(@"<FKUser [name = sun, pass = 123>对应的所有key", [dict allKeysForObject:[[FKUser alloc] initWithName:@"lai" pass:@"123"]]);
//获取遍历dict所有value的枚举器
NSEnumerator* en = [dict objectEnumerator];
NSObject* 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;
}
代码运行结果如下:
2.对NSDictionary的key排序
NSDictionary还提供了方法对NSDictionary的所有key执行排序,这些方法执行完成后返回排序完成后的所有key组成的NSSArray。NSDictionary提供的排序方法如下:
下面展示代码部分:
#import <Foundation/Foundation.h>
#import "FKUser.h"
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//直接使用多个value,key的形式创建NSDictionary对象
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"Object-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);
}
return 0;
}
代码运行结果如下:
3.对NSDictionary的key进行过滤
NSDictionary还提供了方法对NSDictionary的所有key执行过滤,这些方法执行完成后返回满足条件的key组成的NSSet。NSDictionary提供了如下过滤方法:
如下代码所示:
#import <Foundation/Foundation.h>
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//直接使用多个value,key的形式来创建NSDictionary对象
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: 89], @"Object-C",
[NSNumber numberWithInt: 69], @"Ruby",
[NSNumber numberWithInt: 75], @"Python",
[NSNumber numberWithInt: 109], @"Perl",
nil];
//打印dict的集合元素
[dict print];
//对NSDictionary所有key进行过滤
NSSet* keyset = [dict keysOfEntriesPassingTest:
//对NSDictionary的value进行比较,字符串越长即可认为该value越大
^(id key, id value, BOOL *stop ) {
//当value的值大于80时返回YES
//这意味着只有value的值大于80的key才会被保留下来
return (BOOL) ([value intValue] > 80);
}];
NSLog (@"%@", keyset);
}
return 0;
}
代码运行结果如下:
4.使用自定义类作为NSDictionary的key
我们前面介绍我们的key时一般都是使用我们的NSString作为我们的key
如果我们的程序打算使用自定义类来作为我们的NSDictionary的key
我们的正确重写分为以下两步:
- 我们的自定义类正确重写过我们的我们的isEqual以及hash方法,保证当isEqual判断相等时我们的程序返回的hash值也是相等的
- 我们的自定义类必须实现了copyWithZone方法且最好能返回我们的不可变副本。
在这里我们需要解释一下我们为什么需要实现这个方法:
因为我们的key在我们的字典中是非常关键的,出于安全性考虑,我们的key一般是不能改变的。如果我们key本身是可变的,且程序可以通过其他变量来修改key,那么这是非常不好的
为了避免这种情况,所以我们要保证只要程序把任何对象作为key放入我们的字典,我们的字典总会先调用该key的copy方法来复制该对象的不可变副本,然后以该副本作为字典的key,这样就保证了我们的key没有办法被修改。
为了让我们的自定义的类作为key,我们还需要让我们的自定义类实现NSCopying协议,并让该类实现copyWithZone方法。
下面是代码部分:
实现部分:
- (id) copywithZone: (NSZone* )zone {
NSLog(@"--正在复制--");
//
FKUser* newUser = [[[self class] allocWithZone:zone] init];
//
newUser->name = name;
newUser->pass = pass;
return newUser;
}
主函数:
#import <Foundation/Foundation.h>
#import "FKUser.h"
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
FKUser* u1 = [[FKUser alloc] initWithName:@"lai" pass:@"345"];
//直接使用多个value,key的形式来创建NSDictionary对象
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"one", [[FKUser alloc] initWithName:@"gao" pass:@"123"],
@"two", u1,
@"three", [[FKUser alloc] initWithName:@"gao" pass:@"123"],
@"four", [[FKUser alloc] initWithName:@"li" pass:@"178"],
@"five", [[FKUser alloc] initWithName:@"gao" pass:@"155"],
nil];
u1.pass = nil;
//由于NSDictionary并为直接使用u1指向FKUser作为key
//而是先复制u1所指向的对象的副本,然后以该副本作为key
//以此程序将可以看到dict的key不受任何影响
[dict print];
}
return 0;
}
我们上述的程序按value1,key1的格式来设置我们的字典。我们的程序有多对value-key。然后在前面知道了,当程序尝试使用任何对象来作为我们的key时,我们的key都会先调用copy方法来复制该key的不可变副本,这也是我们需要实现copyWithZone:方法的原因。然后我们的程序实际上是将该副本作为我们NSDictionary的key。因此无论我们怎么修改我们的key,他都不会出现变化。
5.NSMutableDictionary的功能和用法
由于该类可以动态的添加key-value对,所以创建该集合时可以指定初始容量。
下面可以通过代码来进一步了解:
#import <Foundation/Foundation.h>
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//直接使用多个value,key的形式来创建NSDictionary对象
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: 89], @"疯狂iOS讲义"
, nil];
//使用下标法设置key-value对。由于NSDictionary已经存在该key
//因此此处设置会覆盖掉鱼啊来的value
dict [@"疯狂iOS讲义"] = [NSNumber numberWithInt: 99];
[dict print];
NSLog(@"---再次添加key-value对---");
dict[@"疯狂XLM讲义"] = [NSNumber numberWithInt: 69];
[dict print];
NSDictionary* dict2 = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: 79], @"疯狂Ajax讲义",
[NSNumber numberWithInt: 89], @"Struts 2.x权威指南",
nil];
//将另一个NSDictionary中的key-value对添加到当前NSDictionary中
[dict addEntriesFromDictionary: dict2];
[dict print];
//根据key来删除key-value对
[dict removeObjectForKey:@"Struts 2.x权威指南"];
[dict print];
}
return 0;
}
代码运行结果如下:
结合上面程序中粗体字代码对NSMutableDictionary中的key-value对的改变,以及上面的输出结果,即可非常容易地体会到NSMutableDictionary中对于key-value对的改变。