SDWebImage學(xué)習(xí)筆記之NSMapTable

NSDictionary/NSMutableArray淺析

我們?cè)谑褂肗SDictionary/NSMutableArray時(shí),通常會(huì)使用NSString對(duì)象作為key考蕾,因?yàn)閗ey必須遵循NSCopying協(xié)議,見(jiàn)NSMutableArray中的方法:

- (void)setObject:(ObjectType)anObject forKey:(KeyType <NSCopying>)aKey;

在NSDictionary/NSMutableArray對(duì)象中船庇,aKey對(duì)象被copy一份后存入懈糯,anObject對(duì)象則被強(qiáng)引用。來(lái)看一段代碼:

NSMutableDictionary *aDictionary = [[NSMutableDictionary alloc] initWithCapacity:0];
{
    NSString *aKey = @"akey";
    NSObject *aObject = [[NSObject alloc] init];
    [aDictionary setObject:aObject forKey:aKey];
}
NSLog(@"dictionary: %@", aDictionary);

打印日志:

dictionary: {
    akey = "<NSObject: 0x60400001d3b0>";
}

本來(lái)作用域結(jié)束后搔扁,aKey變量指向的NSString對(duì)象(簡(jiǎn)稱(chēng)aKey對(duì)象)和aObject變量指向的NSObject對(duì)象(簡(jiǎn)稱(chēng)aObject對(duì)象)應(yīng)該被自動(dòng)釋放,但是aDictionary變量指向的NSMutableDictionary對(duì)象(簡(jiǎn)稱(chēng)aDictionary對(duì)象)持有一份aObject對(duì)象的強(qiáng)引用蟋字,所以打印日志時(shí)阁谆,aDictionary對(duì)象不為空。

現(xiàn)在有一個(gè)Teacher類(lèi)表示班主任信息愉老,包含姓名name屬性和年齡age屬性,另有一個(gè)Student類(lèi)表示學(xué)生信息剖效,也包含姓名name屬性和年齡age屬性嫉入。那么一個(gè)班包含一個(gè)班主任(Teacher對(duì)象)和n個(gè)學(xué)生(Student數(shù)組),為了統(tǒng)計(jì)一個(gè)班的信息璧尸,需要把班主任和學(xué)生的信息及對(duì)應(yīng)關(guān)系保存下來(lái)咒林。

// Teacher.h
@interface Teacher : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

// Teacher.m
@implementation Teacher

@end
// Student.h
@interface Student : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

// Student.m
@implementation Student

@end
// ViewController.m
{
    NSMutableDictionary *aDictionary = [[NSMutableDictionary alloc] initWithCapacity:0];
    Teacher *teacher = [[Teacher alloc] init];
    teacher.name = @"teacher";
    teacher.age = 30;
    NSMutableArray *aArray = [[NSMutableArray alloc] initWithCapacity:0];
    for (int i = 0; i < 3; i++) {
        Student *student = [[Student alloc] init];
        student.name = [NSString stringWithFormat:@"student%d", i];
        student.age = i;
        [aArray addObject:student];
    }
    [aDictionary setObject:aArray forKey:teacher.name];
    NSLog(@"%@", aDictionary);
}

打印日志:

 dictionary:{
    teacher =     (
        "<Student: 0x60000003aa00>",
        "<Student: 0x60000003a9c0>",
        "<Student: 0x60000003aa80>"
    );
}

這里將班主任的姓名作為key,這樣會(huì)損失其他信息爷光。如果想要將teacher對(duì)象作為key垫竞,則需要讓Teacher類(lèi)遵循NSCopying協(xié)議,而且NSDictionary/NSMutable使用hash表來(lái)實(shí)現(xiàn)key和value之間的映射和存儲(chǔ),所以作為key值的類(lèi)型必須重寫(xiě)hash和isEqual:兩個(gè)方法欢瞪,其中hash方法計(jì)算該對(duì)象的hash值活烙,hash值決定該對(duì)象在hash表中存儲(chǔ)的位置,isEqual方法通過(guò)hash值來(lái)定位對(duì)象在hash表中的位置遣鼓。具體代碼如下:

// Teacher.h
@interface Teacher : NSObject<NSCopying>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

// Teacher.m
@implementation Teacher

- (id)copyWithZone:(NSZone *)zone
{
    Teacher *teacher = [[Teacher allocWithZone:zone] init];
    teacher.name = self.name;
    teacher.age = self.age;
    return teacher;
}

- (BOOL)isEqual:(id)object
{
    // 比較hash值是否相等
    return [self hash] == [object hash];
}

- (NSUInteger)hash
{
    // 調(diào)用父類(lèi)的hash方法啸盏,也可以自定義
    return [super hash];
}

打印日志:

dictionary:{
    "<Teacher: 0x600000027940>" =     (
        "<Student: 0x60c00022fa20>",
        "<Student: 0x60c00022fa00>",
        "<Student: 0x60c00022f960>"
    );
}

NSMapTable淺析

NSMapTable繼承自NSObject,自iOS6.0開(kāi)始使用骑祟,NSMapTable是可變的回懦。

NS_CLASS_AVAILABLE(10_5, 6_0)
@interface NSMapTable<KeyType, ObjectType> : NSObject <NSCopying, NSCoding, NSFastEnumeration>

NSMapTable有兩個(gè)指定初始化方法和一個(gè)便捷初始化方法:

// 指定初始化方法
- (instancetype)initWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions capacity:(NSUInteger)initialCapacity NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithKeyPointerFunctions:(NSPointerFunctions *)keyFunctions valuePointerFunctions:(NSPointerFunctions *)valueFunctions capacity:(NSUInteger)initialCapacity NS_DESIGNATED_INITIALIZER;
// 便捷初始化方法
+ (NSMapTable<KeyType, ObjectType> *)mapTableWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions;

初始化方法方法中有兩個(gè)參數(shù)keyOptions和valueOptions,都是NSPointerFunctionsOptions類(lèi)型次企,NSPointerFunctionsOptions是一個(gè)枚舉類(lèi)型怯晕,

typedef NS_OPTIONS(NSUInteger, NSPointerFunctionsOptions) {
    // Memory options are mutually exclusive
    
    // default is strong
    NSPointerFunctionsStrongMemory API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (0UL << 0),       // use strong write-barrier to backing store; use GC memory on copyIn
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || TARGET_OS_WIN32
    NSPointerFunctionsZeroingWeakMemory NS_ENUM_DEPRECATED_MAC(10_5, 10_8) = (1UL << 0),  // deprecated; uses GC weak read and write barriers, and dangling pointer behavior otherwise 
#endif
    NSPointerFunctionsOpaqueMemory API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (2UL << 0),
    NSPointerFunctionsMallocMemory API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (3UL << 0),       // free() will be called on removal, calloc on copyIn
    NSPointerFunctionsMachVirtualMemory API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (4UL << 0),
    NSPointerFunctionsWeakMemory API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0)) = (5UL << 0),         // uses weak read and write barriers appropriate for ARC
    
    // Personalities are mutually exclusive
    // default is object.  As a special case, 'strong' memory used for Objects will do retain/release under non-GC
    NSPointerFunctionsObjectPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (0UL << 8),         // use -hash and -isEqual, object description
    NSPointerFunctionsOpaquePersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (1UL << 8),         // use shifted pointer hash and direct equality
    NSPointerFunctionsObjectPointerPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (2UL << 8),  // use shifted pointer hash and direct equality, object description
    NSPointerFunctionsCStringPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (3UL << 8),        // use a string hash and strcmp, description assumes UTF-8 contents; recommended for UTF-8 (or ASCII, which is a subset) only cstrings
    NSPointerFunctionsStructPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (4UL << 8),         // use a memory hash and memcmp (using size function you must set)
    NSPointerFunctionsIntegerPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (5UL << 8),        // use unshifted value as hash & equality

    NSPointerFunctionsCopyIn API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (1UL << 16),      // the memory acquire function will be asked to allocate and copy items on input
};

常用的枚舉值及對(duì)應(yīng)的含義如下:

  1. NSPointerFunctionsStrongMemory: 強(qiáng)引用存儲(chǔ)對(duì)象
  2. NSPointerFunctionsWeakMemory: 弱引用存儲(chǔ)對(duì)象
  3. NSPointerFunctionsCopyIn:copy存儲(chǔ)對(duì)象

就是說(shuō),如果NSMapTable的初始化方法為:

NSMapTable *aMapTable = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsCopyIn valueOptions:NSPointerFunctionsStrongMemory capacity:0];
或
NSMapTable *aMapTable = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsCopyIn valueOptions:NSPointerFunctionsStrongMemory];

那么就等同于NSMutableDictionay的初始化方法:

NSMutableDictionary *aDictionary = [[NSMutableDictionary alloc] initWithCapacity:0];
或
NSMutableDictionary *aDictionary = [NSMutableDictionary dictionary];

若初始方法修改為

NSMapTable *aMapTable = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory];

即對(duì)key值進(jìn)行弱引用缸棵,就可以不用讓Teacher類(lèi)遵循NSCopying協(xié)議和重新跟hash有關(guān)的兩個(gè)方法舟茶,代碼如下:

// Teacher.h
@interface Teacher : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

// Teacher.m
@implementation Teacher

@end

// ViewController.m
Teacher *teacher = [[Teacher alloc] init];
teacher.name = @"teacher";
teacher.age = 30;NSMutableArray *aArray = [[NSMutableArray alloc] initWithCapacity:0];
for (int i = 0; i < 3; i++) {
    Student *student = [[Student alloc] init];
    student.name = [NSString stringWithFormat:@"student%d", i];
    student.age = i;
    [aArray addObject:student];
}
NSMapTable *aMapTable = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory];
[aMapTable setObject:aArray forKey:teacher];
NSLog(@"%@", aMapTable);

打印日志:

NSMapTable {
[10] <Teacher: 0x604000038a40> -> (
    "<Student: 0x60400003c640>",
    "<Student: 0x60400003c660>",
    "<Student: 0x60400003c620>"
)
}

這樣的方法可以快速的將NSObject對(duì)象作為key存入到“字典”中。

由于NSDictionary/NSMutableArray會(huì)強(qiáng)引用value蛉谜,使得value的引用計(jì)數(shù)+1稚晚,加入不希望怎么做,可以用NSMapTable來(lái)實(shí)現(xiàn)型诚。

NSMapTable *aMapTable = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory];
{
    NSObject *keyObject = [[NSObject alloc] init];
    NSObject *valueObject = [[NSObject alloc] init];
    [aMapTable setObject:valueObject forKey:keyObject];
    NSLog(@"NSMapTable:%@", aMapTable);
}
NSLog(@"NSMapTable:%@", aMapTable);

打印日志:

NSMapTable:NSMapTable {
[6] <NSObject: 0x60c00000c690> -> <NSObject: 0x60c00000c730>
}
NSMapTable:NSMapTable {
}

第一個(gè)NSLog打印出了key-value值客燕,等到object對(duì)象指向的NSObject對(duì)象超出作用域,釋放該對(duì)象狰贯,由于aMapTable弱引用object對(duì)象也搓,aMapTable的中的key-value值會(huì)被安全的刪除,第二個(gè)NSLog打印出的值為空涵紊。


NSMapTable與NSDictionary/NSMutableDictionary對(duì)比

  1. NSDcitionary有一個(gè)可變類(lèi)型NSMutableDictionary傍妒,NSMapTable沒(méi)有可變類(lèi)型,它本身就是可變的;
  2. NSDcitionary/NSMutableDictionary中對(duì)于key和value的內(nèi)存管理方法唯一摸柄,即對(duì)key進(jìn)行copy颤练,對(duì)value進(jìn)行強(qiáng)引用,而NSMapTable沒(méi)有限制驱负;
  3. NSDcitionary中對(duì)key值進(jìn)行copy嗦玖,不可改變,通常用字符串作為key值跃脊,只是key->object的映射宇挫,而NSMapTable的key是可變的對(duì)象,既可以實(shí)現(xiàn)key->object的映射酪术,又可以實(shí)現(xiàn)object->object的映射器瘪。

遺留的問(wèn)題

筆者才疏學(xué)淺,對(duì)NSMapTable與NSDictionary的內(nèi)部結(jié)構(gòu)了解不是很深,不清楚key-value是通過(guò)怎么樣的方式綁定起來(lái)的橡疼,假如看到這篇文章的朋友有所了解援所,希望可以指點(diǎn)一二。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末衰齐,一起剝皮案震驚了整個(gè)濱河市任斋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耻涛,老刑警劉巖废酷,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異抹缕,居然都是意外死亡澈蟆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)卓研,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)趴俘,“玉大人,你說(shuō)我怎么就攤上這事奏赘×壬粒” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵磨淌,是天一觀的道長(zhǎng)疲憋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)梁只,這世上最難降的妖魔是什么缚柳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮搪锣,結(jié)果婚禮上秋忙,老公的妹妹穿的比我還像新娘。我一直安慰自己构舟,他們只是感情好灰追,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著狗超,像睡著了一般弹澎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抡谐,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音桐猬,去河邊找鬼麦撵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的免胃。 我是一名探鬼主播音五,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼羔沙!你這毒婦竟也來(lái)了躺涝?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扼雏,失蹤者是張志新(化名)和其女友劉穎坚嗜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體诗充,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苍蔬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝴蜓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碟绑。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖茎匠,靈堂內(nèi)的尸體忽然破棺而出格仲,到底是詐尸還是另有隱情,我是刑警寧澤诵冒,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布凯肋,位于F島的核電站,受9級(jí)特大地震影響造烁,放射性物質(zhì)發(fā)生泄漏否过。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一惭蟋、第九天 我趴在偏房一處隱蔽的房頂上張望苗桂。 院中可真熱鬧,春花似錦告组、人聲如沸煤伟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)便锨。三九已至,卻和暖如春我碟,著一層夾襖步出監(jiān)牢的瞬間放案,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工矫俺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吱殉,地道東北人掸冤。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像友雳,于是被迫代替她去往敵國(guó)和親稿湿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,101評(píng)論 1 32
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,386評(píng)論 8 265
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉押赊,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,715評(píng)論 0 9
  • 1. 設(shè)計(jì)個(gè)性字母卡饺藤。 2. 用英文制作自己的家庭介紹海報(bào)以及新年賀卡。 3. 下載“英語(yǔ)趣配音”APP流礁,上傳自己...
    Tinawst閱讀 3,089評(píng)論 0 0
  • 今天是穆斯林的開(kāi)齋節(jié)涕俗,早晨六點(diǎn)就加班上任務(wù)來(lái)了,站了不到四個(gè)小時(shí)崇棠,可把我累壞了
    易如人生閱讀 107評(píng)論 0 0