iOS開發(fā)之runtime(25):maptable之增刪查改實(shí)現(xiàn)

logo

本系列博客是本人的源碼閱讀筆記,如果有 iOS 開發(fā)者在看 runtime 的,歡迎大家多多交流。為了方便討論,本人新建了一個(gè)微信群(iOS技術(shù)討論群),想要加入的,請(qǐng)?zhí)砑颖救宋⑿牛簔hujinhui207407,【加我前請(qǐng)備注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,歡迎一起討論

本文完整版詳見筆者小專欄:https://xiaozhuanlan.com/runtime

背景

上一篇文章中筆者和大家分析了 maptable 的頭文件。今天我們來看一下 maptable 的具體實(shí)現(xiàn)。

在開始分析之前,我們先全局搜索一下, maptable 幾個(gè)方法的使用:

  1. 創(chuàng)建


//此 maptable 是為了存儲(chǔ) 需要調(diào)用 initialize 的方法?
pendingInitializeMap = 
            NXCreateMapTable(NXPtrValueMapPrototype, 10);

//創(chuàng)建了 category_map 是為了存儲(chǔ)所有分類?
category_map = NXCreateMapTable(NXPtrValueMapPrototype, 16);

// nonmeta_class_map is typically small
INIT_ONCE_PTR(nonmeta_class_map, 
                  NXCreateMapTable(NXPtrValueMapPrototype, 32), 
                  NXFreeMapTable(v));

// future_named_class_map is big enough for CF's classes and a few others
future_named_class_map = 
        NXCreateMapTable(NXStrValueMapPrototype, 32);

// remapped_class_map is big enough to hold CF's classes and a few others
INIT_ONCE_PTR(remapped_class_map, 
                  NXCreateMapTable(NXPtrValueMapPrototype, 32), 
                  NXFreeMapTable(v));

INIT_ONCE_PTR(protocol_map, 
                  NXCreateMapTable(NXStrValueMapPrototype, 16), 
                  NXFreeMapTable(v) );
gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);

由以上代碼可以看出,runtime 中一共使用了7個(gè) maptable :
pendingInitializeMap 、 category_map 、nonmeta_class_map 、future_named_class_map 、remapped_class_map 、protocol_map 、gdb_objc_realized_classes
這些 maptable 的作用這里先不展開講了,因?yàn)槊總€(gè) maptable 都涉及到非常多的邏輯。目前大家有個(gè)大概認(rèn)識(shí)就好。

分析

寫一個(gè)方法,如下:

void demoMethod(void)
{
    NXMapTable *maptable = NXCreateMapTable(NXPtrValueMapPrototype, 16);
    int a = 1000;
    int* p = &a ;
    int* a1 = (int *)NXMapGet(maptable, "kyson");
    printf("%p",a1);

    NXMapInsert(maptable, "kyson", p);
    
    int * b = (int *)NXMapGet(maptable, "kyson");
    int b1 = *b;
    printf("%i",b1);
    
    NXMapRemove(maptable,"kyson");
    int *c = (int *)NXMapGet(maptable, "kyson");
    printf("%p",c);
}

這段代碼覆蓋了創(chuàng)建、插入、移除、獲取操作,基本可以滿足我們的需求了。那么這段代碼放在哪里能運(yùn)行呢?由于 main 文件里缺少相應(yīng)頭文件,因此筆者放在了這里:


或者還是使用筆者剝離出來的項(xiàng)目:https://github.com/zjh171/RuntimeSample

執(zhí)行結(jié)果

下面我們一個(gè)一個(gè)分析:

創(chuàng)建

NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z) {
    NXMapTable          *table = (NXMapTable *)malloc_zone_malloc((malloc_zone_t *)z, sizeof(NXMapTable));
    NXMapTablePrototype     *proto;
    if (! prototypes)
        prototypes = NXCreateHashTable(protoPrototype, 0, NULL);
    if (! prototype.hash || ! prototype.isEqual || ! prototype.free || prototype.style) {
        _objc_inform("*** NXCreateMapTable: invalid creation parameters\n");
        return NULL;
    }
    proto = (NXMapTablePrototype *)NXHashGet(prototypes, &prototype); 
    if (! proto) {
        proto = (NXMapTablePrototype *)malloc(sizeof(NXMapTablePrototype));
        *proto = prototype;
        (void)NXHashInsert(prototypes, proto);
    }
    table->prototype = proto;
    table->count = 0;
    table->nbBucketsMinusOne = exp2u(log2u(capacity)+1) - 1;
    table->buckets = allocBuckets(z, table->nbBucketsMinusOne + 1);
    return table;
}

在這個(gè)方法中,絕大多數(shù)代碼都是用來初始化 table->prototype 的,我們先把這部分全部忽略,分析一下簡(jiǎn)略版本的實(shí)現(xiàn)。

NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z) {
    NXMapTable          *table = (NXMapTable *)malloc_zone_malloc((malloc_zone_t *)z, sizeof(NXMapTable));
    NXMapTablePrototype     *proto;
    ...
    table->prototype = proto;
    table->count = 0;
    table->nbBucketsMinusOne = exp2u(log2u(capacity)+1) - 1;
    table->buckets = allocBuckets(z, table->nbBucketsMinusOne + 1);
    return table;
}

其他的沒什么好說的,nbBucketsMinusOne 有點(diǎn)特殊,
調(diào)用了兩個(gè)函數(shù):

static unsigned log2u(unsigned x) { return (x<2) ? 0 : log2u (x>>1)+1; };
static INLINE unsigned exp2u(unsigned x) { return (1 << x); };

這兩個(gè)函數(shù)看名字我們就能猜到,一個(gè)是對(duì)數(shù),一個(gè)是指數(shù):

對(duì)數(shù):
logarithm 英 [?l?g?r?e?m] 美 [?l?:g?r?e?m]

指數(shù):
exponent 英 [?k?sp??n?nt] 美 [?k?spo?n?nt]

繼續(xù),我們分析這兩個(gè)函數(shù)的代碼: log2u 函數(shù)是個(gè)遞歸實(shí)現(xiàn),其小于 2 則右移一位,而 exp2u 則相反,是左移一位,那么左移右移有什么區(qū)別呢?

移位操作符
<<:左移運(yùn)算,與其對(duì)應(yīng)的有 >> (右移) 。實(shí)現(xiàn)過程是把該變量先變成2進(jìn)制數(shù),然后進(jìn)行移位,在用0補(bǔ)齊。
右移一位,其實(shí)就是 除以 2 的操作。比如如果開始時(shí) a = 10,那么 a>>1 后,a = 5。
左移一位,其實(shí)就是 乘以 2 的操作。比如如果開始時(shí) a = 10,那么 a>>1 后,a = 20。

nbBuckets到這里就分析完成了,很簡(jiǎn)單,大家不用多去考慮,其實(shí)就是一個(gè)擴(kuò)容算法吧。筆者的另外一個(gè)算法專欄里有對(duì)擴(kuò)容做過詳細(xì)的解釋,這里就點(diǎn)到為止了。

nbBucketsMinusOne名字也有意思:nbBuckets 減去 1。

獲取 table->nbBuckets 之后,再初始化 table->nbBucketsMinusOne + 1 大小的內(nèi)存空間。
NXMapTablePrototype 上篇文章已經(jīng)講解過了,存儲(chǔ)了 hash、isEqual 和 free 的函數(shù)指針(用于獲取數(shù)據(jù)的哈希、判斷兩個(gè)數(shù)據(jù)是否相等以及釋放數(shù)據(jù))。

buckets 存放了真正的數(shù)據(jù),賦值的時(shí)候有個(gè)方法: allocBuckets,其源代碼如下:

static INLINE void *allocBuckets(void *z, unsigned nb) {
    MapPair *pairs = 1+(MapPair *)malloc_zone_malloc((malloc_zone_t *)z, ((nb+1) * sizeof(MapPair)));
    MapPair *pair = pairs;
    while (nb--) { pair->key = NX_MAPNOTAKEY; pair->value = NULL; pair++; }
    return pairs;
}

而 MapPair 的定義如下:

typedef struct _MapPair {
    const void  *key;
    const void  *value;
} MapPair;

可以看出,這里分配的 Buckets 大小其實(shí)就是 MapPair 的個(gè)數(shù)乘以每一個(gè) MapPair 大小的內(nèi)存空間。創(chuàng)建相應(yīng)對(duì)象后,再講 key 值為 NX_MAPNOTAKEY ,value 置為 NULL 。
由于 malloc_zone_malloc 分配的是一塊連續(xù)的內(nèi)存,所以,我們可以理解為 allocBuckets 方法分配了相應(yīng)數(shù)量的 MapPair,每個(gè) MapPair 空間上是連續(xù)的,也就是說我們可以把 nbBuckets 看做一個(gè) MapPair 的數(shù)組。

本文詳細(xì)版請(qǐng)見:
https://xiaozhuanlan.com/topic/4352197806

廣告

我的首款個(gè)人開發(fā)的APP壁紙寶貝上線了,歡迎大家下載。

壁紙寶貝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容