文章目錄
對於工程中使用的弱引用集合的特性進行整理,有些點還是容易忘記
NSMapTable NSHashTable NSPointerArray
是iOS中用於存儲弱引用對象方式,但不僅於此
- NSMapTable 對應 NSMutableDictionary
- NSHashTable 對應 NSMutableSet
- NSPointerArray 對應 NSMutableArray
NSMapTable
NSDictionary的侷限性
- Key必須爲支持
NSCopying
協議的OC對象
,NSDictonary會將設置的Key拷貝到自己的私有空間。
意味着你可以使用NSNumber或者NSString作爲NSDictionary的Key
- Key必須小而高效,以保證拷貝複製的時候不會造成CPU和內存的負擔。
- 會保持對Object的強引用,即Object的引用計數+1。
NSMapTable優勢
-
能夠處理
obj->obj
的映射 -
能夠對Key和value保持弱引用,當key或者value被釋放的時候,此entry對會自動從
NSMapTable
中移除。 -
能夠以包含任意指針對象
配置參數 Options
NSMapTable
既可以處理NSDictionary key -> obj
式的映射,也可以處理 obj->obj
式的映射,根據不同的創建方式,我們可以構建不同的映射關係:
- 像 NSDictionary 一樣,複製
key
,並對它的object
引用計數 +1
NSMapTable *keyToObjectMapping =
[NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn //對key拷貝
valueOptions:NSMapTableStrongMemory]; //強引用object
- 真正的對象到對象(object-to-object)的映射
NSMapTable *objectToObjectMapping = [NSMapTable mapTableWithStrongToStrongObjects];
NSMapTable 的配置都來自枚舉 NSPointerFunctionsOptions
,其中 NSMapTable 可以使用的值如下
- Memory Option
-
NSMapTableStrongMemory
=NSPointerFunctionsStrongMemory
指定相應的key或者value爲強引用 -
NSMapTableWeakMemory
=NSPointerFunctionsWeakMemory
指定相應的key或者value爲弱引用
當不指定時,NSMapTableStrongMemory 是默認的 “memory option”
- Personality Option
用於對比時採用的規則,例如是比較指針,還是比較Int值,還是cstring
NSMapTableObjectPointerPersonality
=NSPointerFunctionsObjectPointerPersonality
此選項是直接使用指針進行isEqual:和hash
- Copy Option
NSMapTableCopyIn
=NSPointerFunctionsCopyIn
指定相應的key或者value在增加到集合中的時候爲copy
你可以在 NSMapTable.h
查看
Memory Option
ARC下
當設置爲 NSMapTableWeakMemory
時,key或者value被釋放的時候,此entry對會自動從NSMapTable
中移除。
NSMapTable *map = [[NSMapTable alloc] initWithKeyOptions:NSMapTableObjectPointerPersonality|NSMapTableWeakMemory
valueOptions:NSMapTableWeakMemory
capacity:20];
@autoreleasepool {
NSObject *key = [NSObject new];
NSLog(@"map key : %@",key);
NSObject *value = [NSObject new];
NSLog(@"map value : %@",value);
[map setObject:value forKey:key];
}
NSLog(@"map enties : %@",map);
日誌輸出爲:
2020-06-18 14:20:28.206834+0800 GIOTest[54945:4954293] map key : <NSObject: 0x6000023943a0>
2020-06-18 14:20:28.206954+0800 GIOTest[54945:4954293] map value : <NSObject: 0x600002398750>
2020-06-18 14:20:28.207069+0800 GIOTest[54945:4954293] map enties : NSMapTable {
}
MRC下
- 當設置爲
NSMapTableWeakMemory
時,不會調用 retain 和 release - 當設置爲
NSMapTableStrongMemory
時,會調用 retain 和 release
同時需要保證 Personality options 的配置滿足是 Objective-C objects 類型。
Personality options
NSMapTableObjectPointerPersonality
用指針來代替實際的值,當對象加入集合中時,會調用其isEqualTo:
和hash
方法呢?,還是會直接對指針Pointer
直接位移進行計算散列值,並直接判斷指針是否相等
- 如果option中設置
NSMapTableObjectPointerPersonality
,那麼會直接對指針Pointer
直接位移進行計算散列值,並直接判斷指針是否相等 - 如果option中沒有設置
NSMapTableObjectPointerPersonality
(默認情況),那麼會調用對象的isEqualTo:
和hash
方法,來判斷是否相等。
當打印這個指針的時候相當於調用description方法,我們重寫description方法,打印如下:
2020-06-18 14:35:32.347062+0800 GIOTest[55031:4964477] map key : my description 0x6000038f0330
2020-06-18 14:35:32.347166+0800 GIOTest[55031:4964477] map value : my description 0x6000038e41a0
2020-06-18 14:35:32.347300+0800 GIOTest[55031:4964477] map enties : NSMapTable {
[18] my description 0x6000038f0330 -> my description 0x6000038e41a0
}
Usage
快捷生成
可以使用自帶類方法生成各個類型的NSMapTable
// strong -> strong
+ (NSMapTable<KeyType, ObjectType> *)strongToStrongObjectsMapTable API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0));
// weak -> strong
+ (NSMapTable<KeyType, ObjectType> *)weakToStrongObjectsMapTable API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0)); // entries are not necessarily purged right away when the weak key is reclaimed
// strong -> weak
+ (NSMapTable<KeyType, ObjectType> *)strongToWeakObjectsMapTable API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0));
// weak -> weak
+ (NSMapTable<KeyType, ObjectType> *)weakToWeakObjectsMapTable API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0)); // entries are not necessarily purged right away when the weak key or object is reclaimed
自定義配置
- Key爲強引用,並對指針進行hash計算判斷是否相等,Value保持弱引用
[[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
| NSPointerFunctionsObjectPersonality
valueOptions:NSPointerFunctionsWeakMemory
capacity:2];
- Key爲強引用,並對指針進行hash計算判斷是否相等,Value保持強引用
[[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality
valueOptions:NSPointerFunctionsStrongMemory
capacity:3];
NSHashTable
在理解 NSMapTable
的基礎上,理解 NSHashTable
NSHashTable 優勢
- 可以持有weak類型的成員變量
- 可以隨意的存儲指針並且利用指針的唯一性來進行hash同一性檢查
配置參數
NSHashTableStrongMemory
=NSPointerFunctionsStrongMemory
對成員變量進行強引用。這是一個默認值。如果採用這個默認值,NSHashTable
和NSSet
就沒什麼區別了。NSHashTableWeakMemory
=NSPointerFunctionsWeakMemory
對成員變量進行弱引用NSHashTableCopyIn
=NSPointerFunctionsCopyIn
加入到集合中之前複製NSHashTableObjectPointerPersonality
=NSPointerFunctionsObjectPointerPersonality
用指針來等同代替實際的值,當打印這個指針的時候相當於調用description方法
Usage
使用 NSHashTable
一般都是使用其弱引用關係
- 創建弱引用關係,並存儲指針
[[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory |
NSPointerFunctionsObjectPointerPersonality
capacity:100];
- 類方法創建
[NSHashTable weakObjectsHashTable]
NSPointerArray
NSPointerArray 特性
- 與NSMutableArray一樣,使用下標有序的插入或移除元素,且可修改數組內容
- 可以插入或刪除nil,並且 nil 參與 count 的計算
- count 可以 set,如果直接 set count,那麼會使用 nil 佔位
- 可以使用 weak 來修飾成員
- 成員可以是所有指針類型
- 遵循 NSFastEnumeration,可以通過 for…in 來進行遍歷
- 當options配置 Personality options 爲對象使用時,NSCopying 和 NSSecureCoding 協議才適用
- 當options配置爲
NSPointerFunctionsWeakMemory
,對象釋放的時候,並不會從數組中刪除,count不會改變
創建
// 根據指定選項返回新指針數組 - 強引用
NSPointerArray *pointerArray = [[NSPointerArray alloc]initWithOptions:NSPointerFunctionsStrongMemory];
// 根據指定函數返回新指針數組 - 弱引用
NSPointerFunctions *functions = [[NSPointerFunctions alloc]initWithOptions:NSPointerFunctionsWeakMemory];
NSPointerArray *pointerArray1 = [[NSPointerArray alloc]initWithPointerFunctions:functions];
// 返回一個強引用元素的數組
NSPointerArray *pointerArray2 = [NSPointerArray strongObjectsPointerArray];
// 返回一個弱引用元素的數組
NSPointerArray *pointerArray3 = [NSPointerArray weakObjectsPointerArray];
Usage
// 設置數組元素數量
pointerArray.count = 5;
// 數組中元素數量
NSUInteger count = [pointerArray count];//5 @[nil,nil,nil,nil,nil]
// 指定索引處的指針
void *point = [pointerArray pointerAtIndex:0];//nil
// 數組中添加指針對象
[pointerArray addPointer:@"2"];//實際打印輸出(2) 內部存儲結構爲 @[nil,nil,nil,nil,nil,@"2"]
// 移除指定索引處的元素
[pointerArray removePointerAtIndex:0];//實際打印輸出() 內部存儲結構爲@[nil,nil,nil,nil,@"2"]
// 指定索引出插入元素
[pointerArray insertPointer:@"1" atIndex:0];//實際打印輸出(1,2) 內部存儲結構爲@[@"1",nil,nil,nil,nil,@"2"]
// 替換指定索引處的對象
[pointerArray replacePointerAtIndex:0 withPointer:@"2"];//實際打印輸出(2,2) 內部存儲結構@[@"2",nil,nil,nil,nil,@"2"]
// 刪除數組中的nil值
[pointerArray addPointer:NULL];//僅compact無法清除nil,需要在之前addPointer:NULL
[pointerArray compact]; //此時count=1
//添加自定義對象
CCObject *key = [CCObject new];
[pointerArray addPointer:(__bridge void * _Nullable)(key)];