NSPointerArray、NSMapTable、NSHashTable介绍

NSPointerArray原文 : iOS官方文档 Foundation篇---NSPointerArray - 简书

封装弱引用delegate集合demo: https://github.com/guochaoshun/weakPointerArray

iOS 中有很多种集合类型,最为常见的可能就 NSArray、NSDictionary、NSSet,但其实还有 NSPointerArray、NSMapTable、NSHashTable 等类型,虽然后面三个类型不常见,但是它们能在关键时刻,「救你一命」。 比如你有一个对象,需要对外抛代理方法, 但是一个普通的delegate已经不能满足要求了, 是一个集合来接受代理方法, 直接使用NSArray会导致代理对象的引用计数+1, 此时如果换成NSPointerArray、NSHashTable 就可以在不改变原对象生命周期的情况下完成需求.

先来看看传统的集合类型都有哪些短板:

1.放到集合中的对象,只能强引用

2.如果想要用void *,要先用 NSValue 打包

3.不能放入 nil

而对于 NSPointerArray、NSMapTable、NSHashTable 来说,除了解决以上问题外,还各有各的「特长」,先看看共同的地方 。

集合类型(包括传统集合)都是无法被继承的. 
 

 NSPointerFunctionsOptions,它是个 option,主要分为三大类:

内存管理

NSPointerFunctionsStrongMemory:默认值,强引用成员

NSPointerFunctionsZeroingWeakMemory:已废弃,在 GC 下,弱引用指针,防止悬挂指针

NSPointerFunctionsMallocMemory 与 NSPointerFunctionsMachVirtualMemory: 用于 Mach 的虚拟内存管理

NSPointerFunctionsWeakMemory:弱引用成员

特性,用于标明对象判等方式

NSPointerFunctionsObjectPersonality:hash、isEqual、对象描述

NSPointerFunctionsOpaquePersonality:pointer 的 hash 、直接判等

NSPointerFunctionsObjectPointerPersonality:pointer 的 hash、直接判等、对象描述

NSPointerFunctionsCStringPersonality:string 的 hash、strcmp 函数、UTF-8 编码方式的描述

NSPointerFunctionsStructPersonality:内存 hash、memcmp 函数

NSPointerFunctionsIntegerPersonality:值的  hash

内存标识

NSPointerFunctionsCopyIn:根据第二类的选择,来具体处理。如果是 NSPointerFunctionsObjectPersonality,则根据  NSCopying 来拷贝。

所以在使用时,可以多个组合,比如:需要强引用成员、使用对象方式对比、并且 add 时 copy 对象:

NSPointerArray

类似于数组的集合,但具有更广泛的可用内存语义;继承自NSObject;NSPointerArray具有以下特点:

  • 与NSMutableArray一样,使用下标有序的插入或移除元素,且可修改数组内容;
  • 可以插入或删除nil,并且 nil 参与 count 的计算 , 超出后会自动扩容;
  • count 可以 set,如果直接 set count,那么会使用 nil 占位;
  • 可以使用 weak 来修饰成员;
  • 成员可以是所有指针类型;
  • 遵循 NSFastEnumeration,可以通过 for...in 来进行遍历。

创建和初始化新的指针数组

// 根据指定选项返回新指针数组
NSPointerArray *pointerArray = [[NSPointerArray alloc]initWithOptions:NSPointerFunctionsStrongMemory];

NSPointerFunctions *functions = [[NSPointerFunctions alloc]initWithOptions:NSPointerFunctionsStrongMemory];
// 根据指定函数返回新指针数组
NSPointerArray *pointerArray1 = [[NSPointerArray alloc]initWithPointerFunctions:functions];

// 返回一个强引用元素的数组
NSPointerArray *pointerArray2 = [NSPointerArray strongObjectsPointerArray];

// 返回一个弱引用元素的数组
NSPointerArray *pointerArray3 = [NSPointerArray weakObjectsPointerArray];

管理集合

// 设置数组元素数量 , 会使用 nil 占位 , 超出会自动扩容
pointerArray.count = 5;

// 数组中元素数量
NSUInteger count = [pointerArray count];//5

// 数组中所有对象
[pointerArray allObjects];

// 指定索引处的指针
void *point = [pointerArray pointerAtIndex:0];//nil

// 数组中添加指针对象
[pointerArray addPointer:@"2"];//(2)

// 移除指定索引处的元素
[pointerArray removePointerAtIndex:0];//(2)

// 指定索引出插入元素
[pointerArray insertPointer:@"1" atIndex:0];//(1,2)

// 替换指定索引处的对象
[pointerArray replacePointerAtIndex:0 withPointer:@"2"];//(2,2)

// 删除数组中的nil值
[pointerArray compact];

// 获取数组的功能项
NSPointerFunctions *Functions = [pointerArray pointerFunctions];
// pointerArray有个bug, 
如果不调用[pointerArray addPointer:nil],直接调用-compact, 
不会移除pointerArray弱引用自动置nil的指针,导致pointerArray的下表和pointerArray下标不一致
pointerArray数组存在nil时,而allobject的值不包含nil,
参考链接:https://stackoverflow.com/questions/31322290/nspointerarray-weird-compaction

// 使用下面的写法, 可以保证把pointerArray中的所有nil移除掉
[pointerArray addPointer:nil];
[pointerArray compact];

NSMapTable

NSMapTable 是在 NSDictionary 之后的一个可变集合模型化的类 , 主要特点是在存入数据的时候可以设置对象为weak引用,key 可以不用遵循 NSCopying 协议;key 和 value 的内存管理方式可以分开,如:key 是强引用,value 是弱引用;

它们具有以下区别:

1. NSMapTable 会在集合里的对象被回收的时候删除此对象来保持对键 和/或 值保持“弱引用”;
2. 当 NSMapTable 添加一个键值对的时候其键或值可以被复制,也可以使用指针标识来进行相等和散列判断;
3. NSMapTable 可以包含任意指针(其内容不被约束为对象)。

你可以将 NSMapTable 实例配置为对任意指针进行操作,而不仅仅是对象,鼓励使用 C 的 API: void * 指针来操作。 基于对象的 API(例如 setObject:forKey :)将无法在不进行类型转换的情况下对无对象指针操作。
 

配置映射表时,请注意,只有NSMapTableOptions中列出的选项才能保证其余的API能够正常工作,包括复制,归档和快速枚举。虽然其他NSPointerFunctions选项用于某些配置,例如保留任意指针,但并不是所有选项的组合都有效。使用某些组合时映射表可能无法正常工作,甚至可能无法正确初始化。

创建方法


  • - (instancetype)initWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions capacity:(NSUInteger)initialCapacity

    通过指定的选项来初始化NSMapTable对象;

    keyOptions:一个位域,用于指定地NSMapTable中的键的选项。
    valueOptions:一个位域,用于指定地NSMapTable中的值的选项
    initialCapacity:NSMapTable的初始容量。 这只是一个提示; 随后可以根据需要增加和缩小NSMapTable。

    值必须与键中指定的所有索引的条目对应。


  • + (NSMapTable<KeyType,ObjectType> *)mapTableWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions

    同上,只不过没有初始指定容量;


  • - (instancetype)initWithKeyPointerFunctions:(NSPointerFunctions *)keyFunctions valuePointerFunctions:(NSPointerFunctions *)valueFunctions capacity:(NSUInteger)initialCapacity

    使用指定的keyFunctions(NSPointerFunction的一个实例是一个适当的调用函数来管理其他地方持有的指针的引用)来初始化NSMapTable对象;

    keyFunctions:用来管理key的NSPointerFunction;
    valueFunctions:用来管理value的NSPointerFunction;


  • + (NSMapTable<KeyType,ObjectType> *)strongToStrongObjectsMapTable

    返回一个对key & value有强引用的NSMapTable对象;


  • + (NSMapTable<KeyType,ObjectType> *)weakToStrongObjectsMapTable

    返回一个对key弱引用,对value强引用的NSMapTable对象
    不建议使用这种映射表,因为NSMapTable可能会在自动调整大小的时候,将weak key对应的value清空;


  • + (NSMapTable<KeyType,ObjectType> *)strongToWeakObjectsMapTable

    返回一个对value弱引用,对key强引用的NSMapTable对象


  • + (NSMapTable<KeyType,ObjectType> *)weakToWeakObjectsMapTable

    返回一个对value弱引用,对key也弱引用的NSMapTable对象

其实,这么多的初始化方法就对应着四种搭配:

key 为 strong,value 为 strong

key 为 strong,value 为 weak

key 为 weak,value 为 strong

key 为 weak,value 为 weak

当用 weak 修饰 key 或 value 时,有一方被释放,则该键值对移除。

获取里面的值


  • - (ObjectType)objectForKey:(KeyType)aKey

    查找指定key对应的value,如果不存在此key则返回nil;


  • - (NSEnumerator<KeyType> *)keyEnumerator

    返回一个允许访问映射表中每个键的枚举器对象,。
    以下代码片段说明了如何使用该方法:

      NSEnumerator *enumerator = [myMapTable keyEnumerator];
      id value;
    
      while ((value = [enumerator nextObject])) {
       /* code that acts on the map table's keys */
      }
    

    NOTE:使用快速枚举协议中的方法会更有效;(NSFastEnumeration)


  • - (NSEnumerator<ObjectType> *)objectEnumerator

    同上,不过是对value的枚举;


  • @property(readonly) NSUInteger count

    NSMapTable中键值对的个数;

修改里面的值


  • - (void)setObject:(ObjectType)anObject forKey:(KeyType)aKey

    在映射表中添加指定的键值对;


  • - (void)removeObjectForKey:(KeyType)aKey

    删除指定的键值对,如果key不存在则什么都不做;


  • - (void)removeAllObjects

    删除所有的键值对;

根据NSMapTable创建一个字典


  • - (NSDictionary<KeyType,ObjectType> *)dictionaryRepresentation

    返回NSMapTable的NSDictionary表示形式。
    映射表的值和键必须符合NSMutableDictionary中 setObject: forKey: 的所有要求。

获取指针函数


  • @property(readonly, copy) NSPointerFunctions *keyPointerFunctions

    用于管理映射表中key的NSPointerFunctions对象;


  • @property(readonly, copy) NSPointerFunctions *valuePointerFunctions

    用于管理映射表中value的NSPointerFunctions对象;


  • typedef NSUInteger NSMapTableOptions

    用来指定NSMapTable对象中元素(键和值)的行为的常量。

NSHashTable

NSHashTable类似于NSSet,特别的是支持弱引用。区别如下:

1. NSHashTable可以对其内的成员进行弱引用;
2. NSHashTable的成员可以在添加的时候被拷贝一份副本;并且可以控制在将对象添加到NSHashTable中时是否调用对象上的 isEqualTo: 和 hash方法;
3. 它可以包含任意指针(其成员不被约束为对象)。

你可以将 NSHashTable 实例配置为对任意指针进行操作,而不仅仅是对象,鼓励使用 C 的 API: void * 指针来操作。基于对象的 API(例如 addObject:)将无法在不进行类型转换的情况下对无对象指针操作。
 

由于它的一些选项,NSHashTable不是一个集合,因为它可以有不同的行为(例如,如果指定了两个isEqual:相等的字符串可能都被添加)。

配置NSHashTable时,请注意,只有NSHashTableOptions中列出的选项才能保证其余的API能正常工作,包括复制,归档和快速枚举。 虽然其他NSPointerFunctions选项用于某些配置,例如保留任意指针,但并不是所有选项的组合都有效。 使用某些组合,NSHashTable可能无法正常工作,甚至可能无法正确初始化。

  


  • - (instancetype)initWithOptions:(NSPointerFunctionsOptions)options capacity:(NSUInteger)initialCapacity

    根据使用的属性来初始化NSHashTable;

    options:NSHashTable里面元素的支持的选项;
    initialCapacity:NSHashTable可以容纳的元素的初始数量。


  • - (instancetype)initWithPointerFunctions:(NSPointerFunctions *)functions capacity:(NSUInteger)initialCapacity

便利构造器


  • + (NSHashTable<ObjectType> *)weakObjectsHashTable

    返回一个用于存储对其内容的弱引用的新NSHashTable对象。

    一个新的NSHashTable,它使用NSPointerFunctionsWeakMemory和NSPointerFunctionsObjectPersonality,初始容量为0。


  • + (NSHashTable<ObjectType> *)hashTableWithOptions:(NSPointerFunctionsOptions)options

    使用给定的NSPointerFunctionsOptions去初始化NSHashTable;

获取里面的值


  • @property(nonatomic, readonly) ObjectType anyObject

    NSHashTable对象里面的一个数据;
    如果哈希表不包含对象,则为nil。

    返回的对象不能保证是随机的。


  • @property(readonly, copy) NSArray<ObjectType> *allObjects

    返回NSHashTable中的所有数据;


  • @property(readonly, copy) NSSet<ObjectType> *setRepresentation

    NSHashTable转为NSSet的形式;


  • @property(readonly) NSUInteger count

    NSHashTable中元素的数量;


  • - (BOOL)containsObject:(ObjectType)anObject

    给定的对象是否存在在NSHashTable中;

    所使用的相等性比较取决于所选择的选项。 例如,使用NSPointerFunctionsObjectPersonality选项将使用isEqual:方法来判断相等。


  • - (ObjectType)member:(ObjectType)object

    确定NSHashTable是否包含给定的对象,并返回该对象(如果存在),如果不存在返回nil;
    所使用的相等性比较取决于所选择的选项。 例如,使用NSPointerFunctionsObjectPersonality选项将使用isEqual:方法来判断相等。


  • - (NSEnumerator<ObjectType> *)objectEnumerator

    返回包含NSHashTable中所有元素的枚举器对象。
    你可以按照如下方法使用:

      NSEnumerator *enumerator = [myHashTable objectEnumerator];
      id value;
    
      while ((value = [enumerator nextObject])) {
          /* code that acts on the hash table's values */
      }
    

    NOTE:使用快速枚举协议中的方法会更有效;(NSFastEnumeration)

  • - (void)addObject:(ObjectType)object

    给hashTable添加指定元素;


  • - (void)removeObject:(ObjectType)object

    删除hashTable中的指定元素;


  • - (void)removeAllObjects

    删除hashTable中的所有元素;

  • - (void)intersectHashTable:(NSHashTable<ObjectType> *)other

    从调用该方法的hashTable中删除不是另一个给定hashTable的成员的每个元素。

    所使用的相等性比较取决于所选择的选项。 例如,使用NSPointerFunctionsObjectPersonality选项将使用isEqual:方法来判断相等。


  • - (BOOL)intersectsHashTable:(NSHashTable<ObjectType> *)other

    返回一个布尔值,指示给定的hashTable是否与调用者有交集。


  • - (BOOL)isSubsetOfHashTable:(NSHashTable<ObjectType> *)other

    返回一个布尔值,指示调用者中的每个元素是否也存在于给定的hashTable中。


  • - (BOOL)isEqualToHashTable:(NSHashTable<ObjectType> *)other

    调用者是否与给定的hashTable相等;
    如果两个hashTable的每个成员具有相同数量的成员,并且如果一个hashTable的每个成员存在于另一个hashTable中,则它们相等。

  • - (void)minusHashTable:(NSHashTable<ObjectType> *)other

    从接收hashTable中删除给定hashTable中的每个元素(如果存在)。


  • - (void)unionHashTable:(NSHashTable<ObjectType> *)other

    将给定hashTable的每个元素添加到接收hashTable(如果不存在)。

  • @property(readonly, copy) NSPointerFunctions *pointerFunctions

    用于管理hashTable中元素的NSPointerFunctions对象;

这类集合类型在 ios6 之后引入,比传统的集合类型更为强大,但是它们的方法却没有传统集合类型多,比如对于 NSPointerArray 来说:

操作均基于 index,无法通过 object 来进行操作;

无法直接插入 array,或用 array 初始化;

查找功能没有 NSArray 强大;

没有逆序、排序等 API 提供

以上几点仅仅是举的例子,所以 NSPointerArray 也并没有看起来的那么强大,一切选择标准,都应该依据具体需求。

所以,还是要先熟悉各种集合的特性,然后去匹配需求,才是最好的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值