NSDictionary實(shí)現(xiàn)原理

NSDictionary介紹

NSDictionary(字典)是使用 hash表來(lái)實(shí)現(xiàn)key和value之間的映射和存儲(chǔ)的挠羔, hash函數(shù)設(shè)計(jì)的好壞影響著數(shù)據(jù)的查找訪問(wèn)效率。數(shù)據(jù)在hash表中分布的越均勻案淋,其訪問(wèn)效率越高矢腻。而在Objective-C中煌张,通常都是利用NSString 來(lái)作為鍵值早歇,其內(nèi)部使用的hash函數(shù)也是通過(guò)使用 NSString對(duì)象作為鍵值來(lái)保證數(shù)據(jù)的各個(gè)節(jié)點(diǎn)在hash表中均勻分布。

NSDictionary內(nèi)部結(jié)構(gòu)

參考cocotron的源代碼憔涉,NSDictionary是使用NSMapTable來(lái)實(shí)現(xiàn)订框。
NSMapTable同樣是一個(gè)key-value的容器,下面是NSMapTable的部分代碼:

typedef struct {
   NSMapTable        *table;
   NSInteger          i;
   struct _NSMapNode *j;
} NSMapEnumerator;

上述結(jié)構(gòu)體描述了遍歷一個(gè)NSMapTable時(shí)的一個(gè)指針對(duì)象兜叨,其中包含table對(duì)象自身的指針穿扳,計(jì)數(shù)值衩侥,和節(jié)點(diǎn)指針。

typedef struct {
   NSUInteger (*hash)(NSMapTable *table,const void *);
   BOOL (*isEqual)(NSMapTable *table,const void *,const void *);
   void (*retain)(NSMapTable *table,const void *);
   void (*release)(NSMapTable *table,void *);
   NSString  *(*describe)(NSMapTable *table,const void *);
   const void *notAKeyMarker;
} NSMapTableKeyCallBacks;

上述結(jié)構(gòu)體中存放的是幾個(gè)函數(shù)指針矛物,用于計(jì)算key的hash值茫死,判斷key是否相等,retain履羞,release操作峦萎。

typedef struct {
   void       (*retain)(NSMapTable *table,const void *);
   void       (*release)(NSMapTable *table,void *);
   NSString  *(*describe)(NSMapTable *table, const void *);
} NSMapTableValueCallBacks;

上述存放的三個(gè)函數(shù)指針,定義在對(duì)nsmaptable插入一對(duì)key-value時(shí)忆首,對(duì)value對(duì)象的操作骨杂。

struct NSMapTable {
   NSMapTableKeyCallBacks   *keyCallBacks;
   NSMapTableValueCallBacks *valueCallBacks;
   NSUInteger             count;
   NSUInteger             nBuckets;
   NSMapNode  **buckets;
};

可以看出來(lái)NSMapTable是一個(gè)哈希+鏈表的數(shù)據(jù)結(jié)構(gòu),因此在NSMapTable中插入或者刪除一對(duì)對(duì)象時(shí)雄卷, 尋找的時(shí)間是O(1)+O(m),m最壞時(shí)可能為n蛤售。

  • O(1):為對(duì)key進(jìn)行hash得到bucket的位置
  • O(m):遍歷該bucket后面沖突的value丁鹉,通過(guò)鏈表連接起來(lái)。

因此NSDictionary中的Key-Value遍歷時(shí)是無(wú)序的悴能,至如按照什么樣的順序揣钦,跟hash函數(shù)相關(guān)。NSMapTable使用NSObject的哈希函數(shù)漠酿。

-(NSUInteger)hash {
   return (NSUInteger)self>>4;
}

上述是NSObject的哈希值的計(jì)算方式冯凹,簡(jiǎn)單通過(guò)移位實(shí)現(xiàn)。右移4位炒嘲,左邊補(bǔ)0宇姚。因?yàn)閷?duì)象大多存于堆中,地址相差4位應(yīng)該很正常夫凸。

NSDictionary的KVC實(shí)現(xiàn)

@implementation NSDictionary (NSKeyValueCoding)
-(id)valueForKey:(NSString*)key;
{
    if([key hasPrefix:@"@"])
        return [super valueForKey:[key substringFromIndex:1]];
    return [self objectForKey:key];
}

-(void)setValue:(id)value forKey:(NSString*)key
{
    [NSException raise:NSInvalidArgumentException format:@"%@ called on immutable dictionary %@", NSStringFromSelector(_cmd), self];
}
@end

@implementation NSMutableDictionary (NSKeyValueCoding)
-(void)setValue:(id)value forKey:(NSString*)key
{
    if(value)
        [self setObject:value forKey:key];
    else
        [self removeObjectForKey:key];
}
@end

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

(1) setValue: ForKey:value是可以為nil的(但是當(dāng)value為nil的時(shí)候夭拌,會(huì)自動(dòng)調(diào)用removeObject:forKey方法)魔熏;setObject: ForKey:value則不可以為nil
(2) setValue: ForKey:key必須是不為nil的字符串類(lèi)型鸽扁;
setObject: ForKey:key可以是不為nil的所有繼承NSCopying的類(lèi)型蒜绽。

NSDictionary copy key對(duì)象

NSDictionary 提供了 key -> object 的映射。從本質(zhì)上講桶现,NSDictionary 中存儲(chǔ)的 object 位置是由 key 來(lái)索引的躲雅。

由于對(duì)象存儲(chǔ)在特定位置,NSDictionary 中要求 key 的值不能改變(否則 object 的位置會(huì)錯(cuò)誤)巩那。為了保證這一點(diǎn)吏夯,NSDictionary 會(huì)始終copy key 到自己私有空間此蜈。而且object對(duì)象在其內(nèi)部是作為強(qiáng)引用(retain或strong);

這個(gè) key 的copy行為也是 NSDictionary 如何工作的基礎(chǔ),但這也有一個(gè)限制:你只能使用 OC 對(duì)象作為 NSDictionary 的 key噪生,并且必須支持 NSCopying 協(xié)議裆赵。此外,key 應(yīng)該是小且高效的跺嗽,以至于復(fù)制的時(shí)候不會(huì)對(duì) CPU 和內(nèi)存造成負(fù)擔(dān)战授。

這意味著,NSDictionary 中真的只適合將值類(lèi)型的對(duì)象作為 key(如簡(jiǎn)短字符串和數(shù)字)桨嫁。并不適合自己的模型類(lèi)來(lái)做對(duì)象到對(duì)象的映射植兰。

參考:
NSDictionary 的內(nèi)部實(shí)現(xiàn)
NSDictionary底層實(shí)現(xiàn)原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市璃吧,隨后出現(xiàn)的幾起案子楣导,更是在濱河造成了極大的恐慌,老刑警劉巖畜挨,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筒繁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡巴元,警方通過(guò)查閱死者的電腦和手機(jī)毡咏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)逮刨,“玉大人呕缭,你說(shuō)我怎么就攤上這事⌒藜海” “怎么了恢总?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)睬愤。 經(jīng)常有香客問(wèn)我离熏,道長(zhǎng),這世上最難降的妖魔是什么戴涝? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任滋戳,我火速辦了婚禮,結(jié)果婚禮上啥刻,老公的妹妹穿的比我還像新娘奸鸯。我一直安慰自己,他們只是感情好可帽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布娄涩。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蓄拣。 梳的紋絲不亂的頭發(fā)上扬虚,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音球恤,去河邊找鬼辜昵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咽斧,可吹牛的內(nèi)容都是我干的堪置。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼张惹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼舀锨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起宛逗,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤坎匿,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后雷激,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體碑诉,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年侥锦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片德挣。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恭垦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出格嗅,到底是詐尸還是另有隱情番挺,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布屯掖,位于F島的核電站玄柏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贴铜。R本人自食惡果不足惜粪摘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绍坝。 院中可真熱鬧徘意,春花似錦、人聲如沸轩褐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)把介。三九已至勤讽,卻和暖如春蟋座,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脚牍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工向臀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人莫矗。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓飒硅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親作谚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子三娩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,089評(píng)論 1 32
  • NSDictionary是基于key - value 方式,把key映射到一個(gè)hash表中實(shí)現(xiàn)的 key 需要支持...
    莊msia閱讀 2,907評(píng)論 2 7
  • NSDictionary(字典)是使用 hash表來(lái)實(shí)現(xiàn)key和value之間的映射和存儲(chǔ)的妹懒, hash函數(shù)設(shè)計(jì)的...
    yymyb閱讀 1,640評(píng)論 0 3
  • NSDictionary介紹 NSDictionary(字典)是使用 hash表來(lái)實(shí)現(xiàn)key和value之間的映射...
    jackyshan閱讀 8,746評(píng)論 3 21
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉雀监,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評(píng)論 0 9