0x01 場(chǎng)景分析
需要?jiǎng)?chuàng)建一個(gè)集合,但是又不想讓集合引用內(nèi)部變量,這樣就可以在內(nèi)部變量被銷(xiāo)毀是,不會(huì)因?yàn)榧弦昧诉@個(gè)變量而導(dǎo)致無(wú)法被銷(xiāo)毀。
如果我們使用NSMutableSet來(lái)進(jìn)行操作,就會(huì)因?yàn)?code>NSMutableSet對(duì)內(nèi)部的元素多一個(gè)強(qiáng)引用導(dǎo)致無(wú)法釋放。那么有沒(méi)有其他集合方案可以讓我們自由控制內(nèi)存引用關(guān)系呢。
在iOS6.0是就已經(jīng)提供了支持,目的是為了拓展集合對(duì)內(nèi)部元素的引用關(guān)系。
NSHashTable 相當(dāng)于NSMutableSet,但是可以對(duì)其內(nèi)部的引用關(guān)系做額外設(shè)置,不像NSMutableSet只是用來(lái)做強(qiáng)引用。
0x02 NSHashTable
NSHashTable允許我們自定義集合對(duì)其內(nèi)部元素的內(nèi)存引用方案。更廣泛意義的NSSet,區(qū)別于NSSet/NSMUtableSet, NSHashTable有以下特性
- 可變
- 可以持有
weak類(lèi)型的成員變量 - 可以再添加成員變量的時(shí)候復(fù)制成員
- 可以任意的存儲(chǔ)指針,并且利用指針的唯一性來(lái)進(jìn)行對(duì)比和重復(fù)檢查。
可行的方案
// 對(duì)成員變量進(jìn)行強(qiáng)引用
NSHashTableStrongMemory / NSPointerFunctionsStrongMemory
// 在ARC下使用弱引用方式進(jìn)行讀寫(xiě),在最后一次釋放后,引用對(duì)象將會(huì)被置為NULL
NSHashTableWeakMemory / NSPointerFunctionsWeakMemory
// 在對(duì)象被加入到集合中前進(jìn)行復(fù)制
NSHashTableCopyIn / NSPointerFunctionsCopyIn
// 用指針來(lái)等同替代實(shí)際的值,當(dāng)打印這個(gè)指針時(shí),相當(dāng)于調(diào)用了description方法
NSHashTableObjectPointerPersonality / NSPointerFunctionsObjectPointerPersonality
// 存儲(chǔ)時(shí),對(duì)要存儲(chǔ)的對(duì)象A進(jìn)行弱引用,當(dāng)A被釋放時(shí),會(huì)自動(dòng)在table中移除
NSHashTable *table = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
// NSHashTable *table = [NSHashTable weakObjectsHashTable]; 功能一樣
Aobj *a = [Aobj new];
[table.addObject:a];
// 強(qiáng)引用,與NSMUtableSet功能一樣
NSHashTable *table = [NSHashTable hashTableWithOptions:NSPointerFunctionsStrongMemory];
Aobj *a = [Aobj new];
[table.addObject:a];
0x03 NSMapTable
NSMapTable是更廣泛意義的NSMutableDictionary,我們可以自定義內(nèi)存管理方案。
NSDictionary/NSMutableDictionary會(huì)賦值keys并且通過(guò)強(qiáng)引用value來(lái)實(shí)現(xiàn)存儲(chǔ)。
NSMapTable和NSDictionary/NSMUtableDictionary相比具有以下特性:
- 可變
- 可以通過(guò)弱引用來(lái)持有
keys和values,所以當(dāng)key或者value被deallocated的時(shí)候,所存儲(chǔ)的實(shí)體也會(huì)被移除。 - 可以在添加
value的時(shí)候?qū)?code>value進(jìn)行復(fù)制 - 可以任意的存儲(chǔ)指針 ,并且利用指針的唯一性來(lái)進(jìn)行對(duì)比和重復(fù)性檢測(cè)。
可行性方案:
// 對(duì)成員變量進(jìn)行強(qiáng)引用
NSMapTableStrongMemory / NSPointerFunctionsStrongMemory
// 對(duì)成員變量進(jìn)行弱引用
NSMapTableWeakMemory
// 在對(duì)象被加入到字典中前進(jìn)行復(fù)制
NSMapTableCopyIn / NSPointerFunctionsCopyIn.
// 用指針來(lái)等同替代實(shí)際的值
NSMapTableObjectPointerPersonality / NSPointerFunctionsObjectPointerPersonality
NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; // value使用copyIn,Person需要實(shí)現(xiàn)NSCopying協(xié)議
{
Person *p = [Person new];
[mapTable setObject:p forKey:@"foo"];
}
NSLog(@"Keys: %@", [[mapTable keyEnumerator] allObjects]);
NSLog(@"values:%@",[mapTable objectForKey:@"foo"]);
注意:NSMapTable不能繼承subscripting
因?yàn)橄聵?biāo)訪(fǎng)問(wèn)需要一個(gè)id作為key,對(duì)于NSMapTable來(lái)說(shuō),這不是強(qiáng)制的。
0x04 NSPointArray
一個(gè)稀疏數(shù)組,與NSMutableArray相似,但可以存儲(chǔ)NULL值。如果存在NULL索引會(huì)發(fā)生改變。
在性能方面,NSPointerArray非常慢,所以當(dāng)你打算在一個(gè)很大的數(shù)據(jù)集合上使用它時(shí)一定要三思。
可行性方案:
// 對(duì)成員變量進(jìn)行強(qiáng)引用
NSPointerFunctionsStrongMemory
// 對(duì)成員變量進(jìn)行弱引用
NSPointerFunctionsWeakMemory
// 在加入集合前執(zhí)行copy
NSPointerFunctionsCopyIn
// 使用對(duì)象的hash和isEqual:(默認(rèn))。
NSPointerFunctionsObjectPersonality
// 對(duì)于isEqual:和hash使用直接的指針比較。
NSPointerFunctionsObjectPointerPersonality
NSPointerArray *ptrArray = [NSPointerArray pointerArrayWithOptions:NSPointerFunctionsWeakMemory];
// NSPointerArray *ptrArray = [NSPointerArray weakObjectsPointerArray]; 效果一樣
for (int i = 0; i < 3; i ++)
{
Person *p = [Person new];
[ptrArray addPointer:(__bridge void *)p];
}
void *a = (__bridge void *)@10;
// void *a = 10; 會(huì)crash,原因就是在
[ptrArray addPointer:a];
NSLog(@"---%@",ptrArray.allObjects);
0x05 NSPointerFunctionsOptions
是一個(gè)option,主要分為三大類(lèi):
內(nèi)存管理語(yǔ)義
// 缺省值,在 CG 和 MRC 下強(qiáng)引用成員
NSPointerFunctionsStrongMemory
// 在指針去除時(shí)不做任何動(dòng)作
NSPointerFunctionsOpaqueMemory
// 用于 Mach 的虛擬內(nèi)存管理
NSPointerFunctionsMallocMemory 與 NSPointerFunctionsMachVirtualMemory
// 在 CG 或者 ARC 下,弱引用成員
NSPointerFunctionsWeakMemory
特性
對(duì)象處理選項(xiàng)即如何進(jìn)行哈希算法,判定同等性,描述
// 使用NSObject的hash, isEqual, description,默認(rèn)
NSPointerFunctionsObjectPersonality
//使用偏移后指針,進(jìn)行hash和直接比較同等性
NSPointerFunctionsOpaquePersonality
// 和上一個(gè)相同,多了description方法
NSPointerFunctionsObjectPointerPersonality
// 使用C字符串的hash、strcmp 函數(shù)、UTF-8 編碼方式的描述
NSPointerFunctionsCStringPersonality
// 使用內(nèi)存的hash、memcmp
NSPointerFunctionsStructPersonality
// 使用偏移量作為hash和等同性判斷
NSPointerFunctionsIntegerPersonality
內(nèi)存標(biāo)識(shí)
// 根據(jù)第二類(lèi)的選擇,來(lái)具體處理
// 如果是 NSPointerFunctionsObjectPersonality,則根據(jù) NSCopying 來(lái)拷貝。
NSPointerFunctionsCopyIn
注意: 每種類(lèi)別的選項(xiàng)只能選擇一個(gè)
所以在使用時(shí),可以多個(gè)組合。比如:需要強(qiáng)引用成員,使用對(duì)象方式對(duì)比,并且add時(shí)copy對(duì)象
NSPointerFunctionsOptions *options = NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality | NSPointerFunctionsCopyIn;
0x06 總結(jié)
正常情況下我們使用NSArray/NSMutableArray,NSSet/NSMutableSet, NSDictionary/NSMutableDictionary就可以達(dá)到90%以上的需求。如果自定義程度更高,我們就可以去考慮使用NSHashTable,NSMapTable,NSPointerArray來(lái)解決問(wèn)題。
參考:
https://blog.csdn.net/u012597860/article/details/51240430
https://www.cnblogs.com/zhaoguowen/p/4273237.html
https://www.cnblogs.com/lxlx1798/p/6651949.html