美文网首页ios底层原理iOS高级技术文章iOS-开发
NSDictionary和NSSet的底层实现原理

NSDictionary和NSSet的底层实现原理

作者: Sweet丶 | 来源:发表于2020-09-16 19:01 被阅读0次

    首先下载源码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的底层实现原理

        本文链接:https://www.haomeiwen.com/subject/csdhyktx.html