iOS 中有很多種集合類型,最為常見的可能就 NSArray、NSDictionary、NSSet,但其實(shí)還有 NSPointerArray、NSMapTable、NSHashTable 等類型,雖然后面三個(gè)類型不常見,但確非常重要。
NSPointerArray
對(duì)應(yīng)著 NSArray,先來看看 API 中介紹的特點(diǎn):
- 和傳統(tǒng) Array 一樣,用于有序的插入或移除;
- 與傳統(tǒng) Array 不同的是,可以存儲(chǔ) NULL,并且 NULL 還參與 count 的計(jì)算;
- 與傳統(tǒng) Array 不同的是,count 可以 set,如果直接 set count,那么會(huì)使用 NULL 占位;
- 可以使用 weak 來修飾成員;
- 成員可以是所有指針類型;
- 遵循 NSFastEnumeration,可以通過 for...in 來進(jìn)行遍歷。
初始化
NSPointerArray 與 NSMutableArray 很像,都是可變有序集合。最大的不同就是它們的初始化方法,NSPointerArray 有兩個(gè)初始化方法:
- (instancetype)initWithOptions:(NSPointerFunctionsOptions)options;
- (instancetype)initWithPointerFunctions:(NSPointerFunctions *)functions;
NSPointerFunctionsOptions
NSPointerFunctionsOptions,它是個(gè) option,主要分為三大類:
- 內(nèi)存管理
NSPointerFunctionsStrongMemory:缺省值,在 CG 和 MRC 下強(qiáng)引用成員
NSPointerFunctionsZeroingWeakMemory:已廢棄,在 GC 下,弱引用指針,防止懸掛指針
NSPointerFunctionsMallocMemory 與 NSPointerFunctionsMachVirtualMemory: 用于 Mach 的虛擬內(nèi)存管理
NSPointerFunctionsWeakMemory:在 CG 或者 ARC 下,弱引用成員 - 特性,用于標(biāo)明對(duì)象判等方式
NSPointerFunctionsObjectPersonality:hash、isEqual、對(duì)象描述
NSPointerFunctionsOpaquePersonality:pointer 的 hash 、直接判等
NSPointerFunctionsObjectPointerPersonality:pointer 的 hash、直接判等、對(duì)象描述
NSPointerFunctionsCStringPersonality:string 的 hash、strcmp 函數(shù)、UTF-8 編碼方式的描述
NSPointerFunctionsStructPersonality:內(nèi)存 hash、memcmp 函數(shù)
NSPointerFunctionsIntegerPersonality:值的 hash - 內(nèi)存標(biāo)識(shí)
NSPointerFunctionsCopyIn:根據(jù)第二類的選擇,來具體處理。如果是 NSPointerFunctionsObjectPersonality,則根據(jù) NSCopying 來拷貝。
NSPointerFunctions
自定義成員的處理方式,如:內(nèi)存管理、hash、isEqual 等,可以看到 API 中定義了一系列屬性,它們都是函數(shù)指針,使用注釋分段.
NSMapTable
與 NSMapTable 對(duì)應(yīng)的,是 NSMutableDictionary。除了 集合的共有特點(diǎn)以外,比起傳統(tǒng)字典,它還有一些優(yōu)勢(shì):
- key 可以不用遵循 NSCopying 協(xié)議;
- key 和 value 的內(nèi)存管理方式可以分開,如:key 是強(qiáng)引用,value 是弱引用;
相比起 NSPointerArray,NSMapTable 的初始化方法要多得多:
#實(shí)例方法,雖然有 capacity 參數(shù),但實(shí)際沒用到
- (instancetype)initWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions capacity:(NSUInteger)initialCapacity;
- (instancetype)initWithKeyPointerFunctions:(NSPointerFunctions *)keyFunctions valuePointerFunctions:(NSPointerFunctions *)valueFunctions capacity:(NSUInteger)initialCapacity;
# 便利構(gòu)造器
+ (NSMapTable<KeyType, ObjectType> *)mapTableWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions;
#返回指定 key、value 內(nèi)存管理類型的 map
+ (NSMapTable<KeyType, ObjectType> *)strongToStrongObjectsMapTable NS_AVAILABLE(10_8, 6_0);
+ (NSMapTable<KeyType, ObjectType> *)weakToStrongObjectsMapTable NS_AVAILABLE(10_8, 6_0);
+ (NSMapTable<KeyType, ObjectType> *)strongToWeakObjectsMapTable NS_AVAILABLE(10_8, 6_0);
+ (NSMapTable<KeyType, ObjectType> *)weakToWeakObjectsMapTable NS_AVAILABLE(10_8, 6_0);
其實(shí),這么多的初始化方法就對(duì)應(yīng)著四種搭配:
- key 為 strong,value 為 strong
- key 為 strong,value 為 weak
- key 為 weak,value 為 strong
- key 為 weak,value 為 weak
當(dāng)用 weak 修飾 key 或 value 時(shí),有一方被釋放,則該鍵值對(duì)移除。
NSHashTable
NSHashTable 對(duì)應(yīng) NSMutableSet,它的 API 更為簡(jiǎn)單,與 NSMapTable 同樣,初始化方法的 capacity 并未生效。
- (instancetype)initWithOptions:(NSPointerFunctionsOptions)options capacity:(NSUInteger)initialCapacity;
- (instancetype)initWithPointerFunctions:(NSPointerFunctions *)functions capacity:(NSUInteger)initialCapacity;
值得注意的是,NSHashTable 有一個(gè) allObjectes 的屬性,返回 NSArray,即使 NSHashTable 是弱引用成員,allObjects 依然會(huì)對(duì)成員進(jìn)行強(qiáng)引用。
應(yīng)用
例如項(xiàng)目中用到藍(lán)牙,藍(lán)牙的連接、發(fā)送、斷開等操作,可以封裝成了單例,但是很多地方都需要檢測(cè)藍(lán)牙當(dāng)前狀態(tài),是否連接、是否發(fā)送成功等等,所以需要多個(gè)回調(diào)。
首先考慮傳值方式:代理、block、通知。通知一般不太考慮,但是代理和 block 對(duì)于單例來說,都只能有一個(gè)值,一個(gè)對(duì)象設(shè)置以后,另一個(gè)就收不到。對(duì)于這種情況,就可以選擇使用 NSHashTable。根據(jù) NSHashTable 不持有對(duì)象,能添加空對(duì)象的特點(diǎn),幾乎完全不會(huì)影響成員的生命周期,也不會(huì)造成 crash。
所以,還是要先熟悉各種集合的特性,然后去匹配需求,才是最好的。