在看KVOController的代碼時,又看到了NSHashTable這個類,所以就此整理一下。
NSHashTable效仿了NSSet(NSMutableSet),但提供了比NSSet更多的操作選項,尤其是在對弱引用關系的支持上,NSHashTable在對象/內存處理時更加的靈活。相較於NSSet,NSHashTable具有以下特性:
NSSet(NSMutableSet)持有其元素的強引用,同時這些元素是使用hash值及isEqual:方法來做hash檢測及判斷是否相等的。
NSHashTable是可變的,它沒有不可變版本。
它可以持有元素的弱引用,而且在對象被銷毀後能正確地將其移除。而這一點在NSSet是做不到的。
它的成員可以在添加時被拷貝。
它的成員可以使用指針來標識是否相等及做hash檢測。
它可以包含任意指針,其成員沒有限制為對象。我們可以配置一個NSHashTable實例來操作任意的指針,而不僅僅是對象。
初始化NSHashTable時,我們可以設置一個初始選項,這個選項確定了這個NSHashTable對象後面所有的行為。這個選項是由NSHashTableOptions枚舉來定義的,如下所示:
enum { // 默認行為,強引用集合中的對象,等同於NSSet NSHashTableStrongMemory = 0, // 在將對象添加到集合之前,會拷貝對象 NSHashTableCopyIn = NSPointerFunctionsCopyIn, // 使用移位指針(shifted pointer)來做hash檢測及確定兩個對象是否相等; // 同時使用description方法來做描述字符串 NSHashTableObjectPointerPersonality = NSPointerFunctionsObjectPointerPersonality, // 弱引用集合中的對象,且在對象被釋放後,會被正確的移除。 NSHashTableWeakMemory = NSPointerFunctionsWeakMemory }; typedef NSUInteger NSHashTableOptions;
當然,我們還可以使用NSPointerFunctions來初始化,但只有使用NSHashTableOptions定義的這些值,才能確保NSHashTable的各個API可以正確的工作—包括拷貝、歸檔及快速枚舉。
個人認為NSHashTable吸引人的地方在於可以持有元素的弱引用,而且在對象被銷毀後能正確地將其移除。我們來寫個示例:
// 具體調用如下 @implementation TestHashAndMapTableClass { NSMutableDictionary *_dic; NSSet *_set; NSHashTable *_hashTable; } - (instancetype)init { self = [super init]; if (self) { [self testWeakMemory]; NSLog(@"hash table [init]: %@", _hashTable); } return self; } - (void)testWeakMemory { if (!_hashTable) { _hashTable = [NSHashTable weakObjectsHashTable]; } NSObject *obj = [[NSObject alloc] init]; [_hashTable addObject:obj]; NSLog(@"hash table [testWeakMemory] : %@", _hashTable); }
這段代碼的輸出結果如下:
hash table [testWeakMemory] : NSHashTable { [6] } hash table [init]: NSHashTable { }
可以看到,在離開testWeakMemory方法,obj對象被釋放,同時對象在集合中的引用也被安全的刪除。
這樣看來,NSHashTable似乎比NSSet(NSMutableSet)要好啊。那是不是我們就應用都使用NSHashTable呢?Peter Steinberger在The Foundation Collection Classes給了我們一組數據,顯示在添加對象的操作中,NSHashTable所有的時間差不多是NSMutableSet的2倍,而在其它操作中,性能大體相近。所以,如果我們只需要NSSet的特性,就盡量用NSSet。
另外,Mattt Thompson在NSHashTable & NSMapTable的結尾也寫了段挺有意思的話,在此直接摘抄過來:
As always, it's important to remember that programming is not about being clever: always approach a problem from the highest viable level of abstraction. NSSet and NSDictionary are great classes. For 99% of problems, they are undoubtedly the correct tool for the job. If, however, your problem has any of the particular memory management constraints described above, then NSHashTable & NSMapTable may be worth a look.
參考
NSHashTable Class Reference
NSHashTable & NSMapTable
NSHashTable & NSMapTable
The Foundation Collection Classes