NSDictionary实现原理-ios哈希hash和isEqual
OC中自定义类的NSCopying实现的注意事项(isEqual & hash实现)
iOS开发 之 不要告诉我你真的懂isEqual与hash!
NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的, hash函数设计的好坏影响着数据的查找访问效率。数据在hash表中分布的越均匀,其访问效率越高。而在Objective-C中,一般都是利用NSString 来做为键值,其内部使用的hash函数也是经过使用 NSString对象做为键值来保证数据的各个节点在hash表中均匀分布。ios
见NSDictionary中最经常使用的一个方法原型:objective-c
- - (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
从这个方法中能够知道, 要做为 Key 值,必须遵循 NSCopying 协议。这是由于在NSDictionary内部,会对 aKey 对象 copy 一份新的。而 anObject 对象在其内部是做为强引用(retain或strong)。
既然知道了做为 key 值,必须遵循 NSCopying 协议,说明除了 NSString 对象以外,咱们还可使用其余类型对象来做为 NSDictionary 的 key值。不过这还不够,做为 key 值,该类型还必须继承于 NSObject 而且要重载一下两个方法:dom
- - (BOOL)isEqual:(id)object;
其中,hash 方法是用来计算该对象的 hash 值,最终的 hash 值决定了该对象在 hash 表中存储的位置。咱们重写hash方法是由于每向NSDictionary和NSSet中存入一个key-value,字典会先利用即将插入的key的hash和字典中已经存在的全部的key.hash进行比较,最终来决定是新增一个key,仍是覆盖原有的key。 可是仅仅使用key.hash比较,有时会出现2个对象hash相同的状况,这时候就须要调用isEqual 方法来最终裁定,2个key对象是否相同。
在OC中,若是自定义类,则要考虑赋值、持久化保存、保存到其它容器中等各类状况的对象复制和比较,下面是一个比较全面的自定义例子,在此仅做记录:ide
自定义类:函数
KeyValuePairs.h:测试
- #import <Foundation/Foundation.h>
-
- @interface KeyValuePairs: NSObject <NSCopying>
- @property (nonatomic,strong)NSString *identifier;
- @property (nonatomic,strong)NSString *name;
-
- @end
KeyValuePairs.m:
- #import "KeyValuePairs.h"
-
- @implementation KeyValuePairs
-
- - (id)copyWithZone:(NSZone *)zone
- {
- KeyValuePairs *kvp = [[[self class] allocWithZone:zone] init];
- kvp.identifier = self.identifier;
- kvp.name = self.name;
- return kvp;
- }
-
- - (BOOL)isEqualToKeyValuePairs:(KeyValuePairs *)kvp{
- if (!kvp) {
- return NO;
- }
- BOOL haveEqualName = (!self.name && !kvp.name) || [self.name isEqualToString:kvp.name];
- BOOL haveEqualIdentifier = (!self.identifier && !kvp.identifier) || [self.identifier isEqualToString:kvp.identifier];
-
- return haveEqualName && haveEqualIdentifier;
- }
-
- #pragma mark -NSObject
- -(BOOL)isEqual:(id)object{
- if (self == object) {
- return YES;
- }
- if (![object isKindOfClass:[KeyValuePairs class]]) {
- return NO;
- }
- return [self isEqualToKeyValuePairs:(KeyValuePairs *)object];
- }
-
- - (NSUInteger)hash {
- return [self.name hash] ^ [self.identifier hash];
- }
-
- @end
测试:
- NSMutableDictionary *namesWillUpdateDic = [[NSMutableDictionary alloc] init];
- NSMutableArray *names = [[NSMutableArray alloc] init];
- for (int i = 0; i<1000; i++) {
- NSString *name = [NSString stringWithFormat:@"%d_zhangsan",i];
- NSString *identifier = [NSString stringWithFormat:@"%d_identifier",i];
- NSString *strObj = [NSString stringWithFormat:@"%d_strObj",i];
- KeyValuePairs *kvp = [[KeyValuePairs alloc] init];
- kvp.identifier = identifier;
- kvp.name = name;
- [namesWillUpdateDic setObject:strObj forKey:kvp];
- [names addObject:kvp];
- }
-
- for (int j = 0; j<1000; j++) {
- int index = arc4random()%1000;
- KeyValuePairs *kvp = [names objectAtIndex:index];
- NSString *strObj = [namesWillUpdateDic objectForKey:kvp];
- NSString *msg = [NSString stringWithFormat:@"index:%d,identifier:%@,email:%@,strObj:%@",index,kvp.identifier,kvp.name,strObj];
- NSLog(@"%@",msg);
- }
一、自定义类为何必定要实现NSCopying协议呢?这是由于经过key-value把2个对象加入到字典中,字典会对key进行copy一份的操做,而对value对象进行retain操做,若是自定义的类不实现copy协议,那么就不能做为字典的key对象使用。
二、若是自定义类不重写isEqual则默认使用内存地址比较两个对象,可能会出现意想不到的结果
三、isEqual和hash方法要同时重写,不然isEqual方法判断将不正确