OC学习记录,字典


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对的增加、删除操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值