首先下载源码NSDictionary \ NSSet,把源码拉到项目中方便查看。源码
一、对象的哈希函数
一个对象的哈希值通过hash方法获得,通过OC源码可以看到OC源码
- (NSUInteger)hash {
return _objc_rootHash(self);
}
uintptr_t _objc_rootHash(id obj)
{
return (uintptr_t)obj;
}
所以OC对象默认的哈希值就是对象的地址(uintptr_t)obj
为了看懂源码究竟在做什么,需要先看一下哈希表的原理:
笔记-数据结构之 Hash
NSSet和NSDictionary底层都是CF...完成的,而且实现原理就是哈希表(数组+开放定址法解决哈希冲突的方式)。
二、NSSet的实现原理
通过源码来查看
void CFSetAddValue(CFMutableSetRef set, const void *key) {
// 上面参数key就是set中要添加的那个对象. void *是任意类型
// 1.当 可变的set中,_count == _capacity 时或者set为空时__CFSetGrow去扩容 __CFSetGrow(set, 1);
if (set->_count == set->_capacity || NULL == set->_keys) {
__CFSetGrow(set, 1);
}
// 2. __CFSetFindBuckets2查找集合set中key是否有match的值,返回的nomatch是可以放置的数组下标
__CFSetFindBuckets2(set, key, &match, &nomatch);
if (kCFNotFound != match) {
// 本身存在这个key,什么也没做
} else {
//3. 将对象的引用计数器+1
cb = __CFSetGetKeyCallBacks(set);
if (cb->retain) {
newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, key, set->_context);
} else {
newKey = key;
}
// 4.将newKey写入到数组_keys中的第nomatch位置。
CF_WRITE_BARRIER_ASSIGN(keysAllocator, set->_keys[nomatch], newKey);
set->_count++;
}
}
上面代码的注释就是实现的步骤,由上面可知,一个集合sets中实现了没有重复的值,因为当找到有匹配的key时什么也没做。
- 数组的扩容时会首先将数组容量扩展,其次将原来数组中的key重新计算下标放到新的数组中。
- 数组扩容的数量按照预先定义的数量来确定,
__CFSetCapacities
中预先确定了怎么扩容。4个-》8个-》17个
. 查看源码可知详情。
static const uint32_t __CFSetCapacities[42] = {
4, 8, 17, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349,
15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498,
3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324,
228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636U
};
三、NSDictionary的实现原理
NSDictionary的原理与NSSet是一样的,只不过是用了keys数组
和values数组
两个数组, 通过源码可以看到,扩容时是同时扩容这两个数组,确定下标时,用key的下标作为values数组中存放值时的下标。不过在key值查找到时是只更新value而不是NSSets的什么也不做。
void CFDictionarySetValue(CFMutableDictionaryRef dict, const void *key, const void *value) {
// 1.可变的dict中,_count == _capacity 时或者dict为空时__CFDictionaryGrow去扩容
if (dict->_count == dict->_capacity || NULL == dict->_keys) {
__CFDictionaryGrow(dict, 1);
}
// 2. __CFDictionaryFindBuckets2查找字典dict中key是否有match的值,返回的nomatch是可以放置的数组下标
__CFDictionaryFindBuckets2(dict, key, &match, &nomatch);
//3. 将新value的引用计数器+1
if (vcb->retain) {
newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), allocator, value, dict->_context);
} else {
newValue = value;
}
if (kCFNotFound != match) {
//4.之前是存在这个key的,先对value前值release
if (vcb->release) {
INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), allocator, dict->_values[match], dict->_context);
}
// 5. 将新的value放到_values数组中第match个元素
CF_WRITE_BARRIER_ASSIGN(valuesAllocator, dict->_values[match], newValue);
} else {
//6.之前是不存在这个key的,先对newKey进行retain,引用计数加1
if (cb->retain) {
newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, key, dict->_context);
} else {
newKey = key;
}
// 7.将newKey添加到_keys数组中nomatch位置,将newValue添加到_values数组中第nomatch位置
CF_WRITE_BARRIER_ASSIGN(keysAllocator, dict->_keys[nomatch], newKey);
CF_WRITE_BARRIER_ASSIGN(valuesAllocator, dict->_values[nomatch], newValue);
dict->_count++;
}
}
网友评论