平时开发中,大部分使用NSSet和NSMutableSet。下面介绍个特殊的。
- NSHashTable
 
- NXHashTable(objc源码)
 
NSHashTable
NSHashTable是更广泛意义的NSSet,区别于NSSet/NSMutableSet,NSHashTable有如下特性:
- NSHashTable 是可变的;
 
- NSHashTable 可以持有 weak 类型的成员变量;
 
- NSHashTable 可以在添加成员变量的时候复制成员;
 
- NSHashTable 可以随意的存储指针并且利用指针的唯一性来进行 hash 同一性检查(检查成员变量是否有重复)和对比操作(equal);
 
参考: https://nshipster.com/nshashtable-and-nsmaptable/
Hash冲突解决方案:拉链法
使用方法
1 2 3 4
   | NSString *str = @"10"; NSHashTable *table = [[NSHashTable alloc] initWithOptions:NSHashTableCopyIn capacity:0]; [table addObject:str]; [table removeObject:str];
   | 
 
NSHashTableOptions介绍(源码中是使用静态常量做关联)
1 2 3 4 5 6 7 8 9 10 11 12
   | enum {          NSHashTableStrongMemory             = 0,          NSHashTableCopyIn                   = NSPointerFunctionsCopyIn,               NSHashTableObjectPointerPersonality = NSPointerFunctionsObjectPointerPersonality,          NSHashTableWeakMemory               = NSPointerFunctionsWeakMemory }; typedef NSUInteger NSHashTableOptions;
  | 
 
NXHashTable
在objc源码中,找到了关于NSHashTable的类似实现。可以参考下
NXHashTable存储结构(精简过)
1 2 3 4 5 6 7 8 9 10 11 12
   | typedef struct {          const NXHashTablePrototype	*prototype;          unsigned		  count;          unsigned		  nbBuckets;          void			 *buckets;          const void	     *info; } NXHashTable;
  | 
 
NXHashTablePrototype存储结构(精简过)
1 2 3 4 5 6 7 8 9 10 11
   | typedef struct {          uintptr_t	(* hash)(const void * info,                                   const void * data);          int		(* isEqual)(const void * info, const void * data1, const void * data2);          void	(* free)(const void * info, void * data);          int		style; } NXHashTablePrototype;
  | 
 
HashBucket存储结构
1 2 3 4 5 6 7 8 9 10 11 12
   | typedef union {          const void	*one;          const void	**many; } oneOrMany; typedef struct	{     unsigned 	count;                oneOrMany	elements; } HashBucket;
  | 
 
NXHashTable的构造器NXCreateHashTable和NXCreateHashTableFromZone
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 29 30 31 32 33 34
   | NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info) {     return NXCreateHashTableFromZone(prototype, capacity, info, DEFAULT_ZONE); }
  NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, void *z) {     NXHashTable			*table;     NXHashTablePrototype	*proto;          table = ALLOCTABLE(z);     if (! prototypes) bootstrap ();     if (! prototype.hash) prototype.hash = NXPtrHash;     if (! prototype.isEqual) prototype.isEqual = NXPtrIsEqual;     if (! prototype.free) prototype.free = NXNoEffectFree;     if (prototype.style) { 	_objc_inform ("*** NXCreateHashTable: invalid style\n"); 	return NULL; 	};     proto = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype);      if (! proto) { 	proto             = (NXHashTablePrototype *) malloc(sizeof (NXHashTablePrototype)); 	bcopy ((const char*)&prototype, (char*)proto, sizeof (NXHashTablePrototype));     	(void) NXHashInsert (prototypes, proto); 	proto = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype); 	if (! proto) { 	    _objc_inform ("*** NXCreateHashTable: bug\n"); 	    return NULL; 	    }; 	};     table->prototype = proto; table->count = 0; table->info = info;     table->nbBuckets = GOOD_CAPACITY(capacity);     table->buckets = ALLOCBUCKETS(z, table->nbBuckets);     return table; }
  | 
 
取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   |  #define	BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) % table->nbBuckets))
  #define ISEQUAL(table, data1, data2) ((data1 == data2) || (*table->prototype->isEqual)(table->info, data1, data2)) void *NXHashGet (NXHashTable *table, const void *data) {     HashBucket	*bucket = BUCKETOF(table, data);     unsigned	j = bucket->count;     const void	**pairs;          if (! j) return NULL;     if (j == 1) {     	return ISEQUAL(table, data, bucket->elements.one) 	    ? (void *) bucket->elements.one : NULL;  	};     pairs = bucket->elements.many;          while (j--) { 	     	if (ISEQUAL(table, data, *pairs)) return (void *) *pairs;  	pairs ++; 	};     return NULL;     }
 
  | 
 
插入数据
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
   | void *NXHashInsert (NXHashTable *table, const void *data) {     HashBucket	*bucket = BUCKETOF(table, data);     unsigned	j = bucket->count;     const void	**pairs;     const void	**newt;     __unused void *z = ZONE_FROM_PTR(table);          if (! j) { 	bucket->count++; bucket->elements.one = data;  	table->count++;  	return NULL; 	};     if (j == 1) {     	if (ISEQUAL(table, data, bucket->elements.one)) { 	       const void	*old = bucket->elements.one; 	       bucket->elements.one = data; 	       return (void *) old; 	   };          	   newt = ALLOCPAIRS(z, 2); 	   newt[1] = bucket->elements.one; 	   *newt = data; 	   bucket->count++; bucket->elements.many = newt;  	   table->count++;  	    	   if (table->count > table->nbBuckets) _NXHashRehash (table); 	   return NULL; 	};     pairs = bucket->elements.many;          while (j--) { 	     	if (ISEQUAL(table, data, *pairs)) { 	       const void	*old = *pairs; 	       *pairs = data; 	       return (void *) old; 	    }; 	   pairs ++; 	};          newt = ALLOCPAIRS(z, bucket->count+1);     if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE);     *newt = data;     FREEPAIRS (bucket->elements.many);     bucket->count++; bucket->elements.many = newt;      table->count++;      if (table->count > table->nbBuckets) _NXHashRehash (table);     return NULL;     }
  | 
 
扩容
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 29 30 31
   | 
 
 
 
  #define MORE_CAPACITY(b) (b*2+1) static void _NXHashRehash (NXHashTable *table) {     _NXHashRehashToCapacity (table, MORE_CAPACITY(table->nbBuckets)); } void _NXHashRehashToCapacity (NXHashTable *table, unsigned newCapacity) {     
      NXHashTable	*old;     NXHashState	state;     void	*aux;     __unused void *z = ZONE_FROM_PTR(table);          old = ALLOCTABLE(z);     old->prototype = table->prototype; old->count = table->count;      old->nbBuckets = table->nbBuckets; old->buckets = table->buckets;     table->nbBuckets = newCapacity;     table->count = 0; table->buckets = ALLOCBUCKETS(z, table->nbBuckets);     state = NXInitHashState (old);     while (NXNextHashState (old, &state, &aux)) 	(void) NXHashInsert (table, aux);     freeBuckets (old, NO);     if (old->count != table->count) 	_objc_inform("*** hashtable: count differs after rehashing; probably indicates a broken invariant: there are x and y such as isEqual(x, y) is TRUE but hash(x) != hash (y)\n");     free (old->buckets);      free (old); }
 
  | 
 
删除某个元素
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
   | void *NXHashRemove (NXHashTable *table, const void *data) {     HashBucket	*bucket = BUCKETOF(table, data);     unsigned	j = bucket->count;     const void	**pairs;     const void	**newt;     __unused void *z = ZONE_FROM_PTR(table);          if (! j) return NULL;     if (j == 1) { 	if (! ISEQUAL(table, data, bucket->elements.one)) return NULL; 	data = bucket->elements.one; 	table->count--; bucket->count--; bucket->elements.one = NULL; 	return (void *) data; 	};     pairs = bucket->elements.many;     if (j == 2) {     	if (ISEQUAL(table, data, pairs[0])) { 	    bucket->elements.one = pairs[1]; data = pairs[0]; 	    } 	else if (ISEQUAL(table, data, pairs[1])) { 	    bucket->elements.one = pairs[0]; data = pairs[1]; 	    } 	else return NULL; 	FREEPAIRS (pairs); 	table->count--; bucket->count--; 	return (void *) data; 	};     while (j--) {     	if (ISEQUAL(table, data, *pairs)) { 	    data = *pairs; 	     	    newt = (bucket->count-1)  		? ALLOCPAIRS(z, bucket->count-1) : NULL; 	    if (bucket->count-1 != j) 		    bcopy ((const char*)bucket->elements.many, (char*)newt, PTRSIZE*(bucket->count-j-1)); 	    if (j) 		    bcopy ((const char*)(bucket->elements.many + bucket->count-j), (char*)(newt+bucket->count-j-1), PTRSIZE*j); 	    FREEPAIRS (bucket->elements.many); 	    table->count--; bucket->count--; bucket->elements.many = newt; 	    return (void *) data; 	    }; 	pairs ++; 	};     return NULL; }
  |