文章目录
对于工程中使用的弱引用集合的特性进行整理,有些点还是容易忘记
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)];