Kealdish's Studio.

深入理解NSMapTable、NSHashTable、NSPointerArray

字数统计: 1.2k阅读时长: 5 min
2016/03/10 Share

最近在学习YYCache中的YYDiskCache时,注意到了这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// weak reference for all instances
static NSMapTable *_globalInstances;
static dispatch_semaphore_t _globalInstancesLock;
static void _YYDiskCacheInitGlobal() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_globalInstancesLock = dispatch_semaphore_create(1);
_globalInstances = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
});
}
static YYDiskCache *_YYDiskCacheGetGlobal(NSString *path) {
if (path.length == 0) return nil;
_YYDiskCacheInitGlobal();
dispatch_semaphore_wait(_globalInstancesLock, DISPATCH_TIME_FOREVER);
id cache = [_globalInstances objectForKey:path];
dispatch_semaphore_signal(_globalInstancesLock);
return cache;
}
static void _YYDiskCacheSetGlobal(YYDiskCache *cache) {
if (cache.path.length == 0) return;
_YYDiskCacheInitGlobal();
dispatch_semaphore_wait(_globalInstancesLock, DISPATCH_TIME_FOREVER);
[_globalInstances setObject:cache forKey:cache.path];
dispatch_semaphore_signal(_globalInstancesLock);
}

YYDiskCache采用NSMapTable作为全局变量而并没有采用NSDictionary来存储cache变量,这样做的好处是避免_globalInstancescache发生循环引用,造成内存无法释放。回顾之前自己的编码经历,基本没考虑过NSDictionary与其元素可能发生的循环引用问题,不得不说自己在这方面还是有欠缺。因而写下这篇blog总结这部分的内容。

我在总结的过程中发现了NSMapTable-NSDictionaryNSHashTable-NSSetNSPointerArray-NSArray三组存在相似的关系,于是我就想干脆将这几个类总结在一起,便于理解。

NSMapTable - NSDictionary

以下是苹果文档关于NSMapTable的描述:

The NSMapTable class is a mutable collection modeled after NSDictionary, with the following differences:

The major option is to have keys and/or values held “weakly” in a manner that entries are removed when one of the objects is reclaimed.

Its keys or values may be copied on input or may use pointer identity for equality and hashing.

It can contain arbitrary pointers (its contents are not constrained to being objects).

总结一下,NSMapTable仿效的是NSDictionary,但它是可变的集合。它与NSDictionary有几点区别:

1.NSMapTableNSDictionary最重要的区别在于前者可以将keys和values以弱引用的方式关联,当其中任一对象被回收了,其所有的内容都会被移除。我们可以通过mapTableWithKeyOptions:valueOptions:分别控制键和值的对象获取/保留行为。NSDictionary则会对keys和values执行retain操作,只有等到NSDictionary被release才会release它所持有的keys和values。

  1. NSMapTable可以包含任意的指针并且指针的内容不限定必须是对象,然后用指针去做相等或者hasing检查,而NSDictionary的key必须是遵循NSCopying协议的对象。不仅如此,如果NSDictionary要使用KVC那么key必须是字符串。
  2. NSMapTable指定为NSMapTableCopyIn,它会通过NSCopying协议将添加进来的数据复制一份副本,NSDictionary则需要调用copy方法来复制数据。

NSHashTable - NSSet

以下是苹果文档关于NSHashTable的描述:

NSHashTable is modeled after NSSet but provides different options, in particular to support weak relationships.

It can hold weak references to its members.

Its members may be copied on input or may use pointer identity for equality and hashing.

It can contain arbitrary pointers (its members are not constrained to being objects).

Because of its options, NSHashTable is not a set because it can behave differently (for example, if pointer equality is specified two isEqual: strings will both be entered).

总结一下,NSHashTable效仿的是NSSet但提供更多不同的选项,尤其是支持weak关联。它支持对所有成员的弱引用,而NSSet对所有成员均是强引用。其他的不同点与前面NSMapTableNSDictionary类似,可以参考对比,就不再码废话了。

NSPointerArray - NSArray

以下是苹果文档关于NSPointerArray的描述:

The NSPointerArray class represents a mutable collection modeled after NSArray, but can also hold nil values. nil values may be inserted or removed and contribute to the object’s count. An NSPointerArray object can also increase and decrease its count directly.

A pointer array can be initialized to maintain strong or weak references to objects, or according to any of the memory or personality options defined by NSPointerFunctionsOptions.

The NSCopying and NSCoding protocols are applicable only when a pointer array is initialized to maintain strong or weak references to objects.

When enumerating a pointer array with NSFastEnumeration using for…in, the loop will yield any nil values present in the array. See Fast Enumeration Makes It Easy to Enumerate a Collection in Programming with Objective-C for more information.

总结一下,NSPointerArray效仿的是NSArray,但可以存储值为nil的元素。nil可以被插入和移除并且计入对象的数目中。而NSArray不可以存储nil,通常它会将nil看做数组的终止符,并且不会计入到对象的数目当中。NSPointerArray可以直接增加或减少元素的数量,与NSMutableArray类似。NSPointerArray最重要的特性与前面两组相同,均是可以对存储的对象采用弱引用。只有当NSPointerArray存储的是对象时NSCopyingNSCoding协议才会适用。还有一点要注意的,在快速遍历的for…in方法中,如果NSPointerArray存在nil则循环遍历会终止。

结语

依据这三组类的不同也就归纳出它们各自的应用场景。若你的使用场景中可能会产生如实例代码中的循环引用问题,那就使用前者,若你的使用场景中存储的对象不遵循NSCopying协议或者就不是对象,而是指针之类,那就使用前者。其他大多数情况下还是使用后者。

CATALOG
  1. 1. NSMapTable - NSDictionary
  2. 2. NSHashTable - NSSet
  3. 3. NSPointerArray - NSArray
  4. 4. 结语