NSDictionary和NSSet的底层实现原理

首先下载源码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++;
    }
}

参考阅读:
笔记-集合NSSet、字典NSDictionary的底层实现原理
笔记-数据结构之 Hash

你可能感兴趣的:(NSDictionary和NSSet的底层实现原理)