NSDictionary实现原理

NSDictionary(字典)是使用 哈希表来实现key和value之间的映射和存储的, hash函数设计的好坏影响着数据的查找访问效率。数据在hash表中分布的越均匀,其访问效率越高。而在Objective-C中,通常都是利用NSString 来作为键值,其内部使用的hash函数也是通过使用 NSString对象作为键值来保证数据的各个节点在hash表中均匀分布。
见NSDictionary中最常用的一个方法原型:

1
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;

NSDictionary中的key是唯一的,key可以是遵循NSCopying 协议和重载- (NSUInteger)hash;- (BOOL)isEqual:(id)object;方法的任何对象。也就是说在NSDictionary内部,会对 aKey 对象 copy 一份新的。而 anObject 对象在其内部是作为强引用(retain或strong)。

hash 方法是用来计算该对象的 hash 值,最终的 hash 值决定了该对象在 hash 表中存储的位置。所以同样,如果想重写该方法,我们尽量设计一个能让数据分布均匀的 hash 函数。
isEqual : 方法是为了通过 hash 值来找到 对象 在hash表中的位置。

在调用setObject: forKey: 后,内部会去调用 key 对象的 hash 方法确定 objecthash表内的入口位置,然后会调用 isEqual : 来确定该值是否已经存在于 NSDictionary中。

补充一个小知识:iOS setValue和setObject的区别:

1
2
- (void)setObject:(ObjectType)anObject forKey:(KeyType <NSCopying>)aKey;
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;

首先:setObject: ForKey:是NSMutableDictionary特有的;setValue: ForKey:是KVC的主要方法。

区别:

(1) setValue: ForKey:的value是可以为nil的(但是当value为nil的时候,会自动调用removeObject:forKey方法);

setObject: ForKey:的value则不可以为nil。

(2) setValue: ForKey:的key必须是不为nil的字符串类型;

setObject: ForKey:的key可以是不为nil的所有继承NSCopying的类型。