2013-02-04 57 views
2

SO answer顯示NSDictionary的散列值是字典中條目的數量。 (Similarly, the hash of an NSArray is its length。)答案繼續建議創建一個類別以提供更好的散列實現。覆蓋NSArray的散列值

如果您需要更準確的散列值,您可以在Obj-C類別中自己提供一個 。

但是,當我嘗試這個,似乎無論如何使用原始的哈希實現。

我們在NSDictionary+Hash.mNSDictionary+Hash.h

#import <Foundation/Foundation.h> 

@interface NSDictionary (Hash) 
- (NSUInteger)hash; 
@end 

頁眉和執行:

#import "NSDictionary+Hash.h" 

@implementation NSDictionary (Hash) 

- (NSUInteger)hash 
{ 
    // Based upon standard hash algorithm ~ https://stackoverflow.com/a/4393493/337735 
    NSUInteger result = 1; 
    NSUInteger prime = 31; 
    // Fast enumeration has an unstable ordering, so explicitly sort the keys 
    // https://stackoverflow.com/a/8529761/337735 
    for (id key in [[self allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 
     id value = [self objectForKey:key]; 
     // okay, so copying Java's hashCode a bit: 
     // http://docs.oracle.com/javase/6/docs/api/java/util/Map.Entry.html#hashCode() 
     result = prime * result + ([key hash]^[value hash]); 
    } 
    return result; 
} 

一個簡單的單元測試顯示了原始的實現是在使用中:

#import "NSDictionary+Hash.h" 

#import <SenTestingKit/SenTestingKit.h> 

@interface NSDictionary_HashTest : SenTestCase 
@end 

@implementation NSDictionary_HashTest 

- (void)testHash 
{ 
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: 
          @"val1", @"key1", @"val2", @"key2", nil]; 
    NSUInteger result = 1; 
    result = 31 * result + ([@"key1" hash]^[@"val1" hash]); 
    result = 31 * result + ([@"key2" hash]^[@"val2" hash]); 
    STAssertEquals([dict hash], result, nil); 
} 

@end 

這測試失敗,「2」應該等於「2949297985」。

現在,如果我在類別標題和實現文件中將方法從哈希重命名爲hashy(例如),那麼[dict hashy]會返回正確的值。是否可以重寫某個類別中的「內置」方法?我在做別的事嗎?

+1

科迪,你絕對可以用類別覆蓋內建的方法。 – xyzzycoder

+0

它曾經是這種情況,我不確定當前的情況,如果你有兩個實現了相同方法名的類,並且這兩個類都是單個類的一部分,那麼鏈接順序決定了哪個方法會被調用由運行時。 Objective-C在過去五年中發生了很大變化,所以現在這種行爲可能會有所不同。 – xyzzycoder

回答

10

NSDictionary是一個類集羣 - 當您將消息發送到NSDictionary時,與之交互的內容永遠不會是NSDictionary的實際實例,而是私有子類的實例。因此,當您覆蓋類別中的hash方法時,它確實在NSDictionary中重寫了該方法,但具體的子類有其自己的hash方法,因此它將覆蓋您的方法。

如果你真的想這樣做,我想你會想檢查類NSDictionaryI和NSDictionaryM的存在,並動態地覆蓋它們的hash方法。但是這在內部實現的細節上是搞亂的,除非你真的處於非常困難的境地,否則我不會推薦這樣做。如果你分析並發現NSDictionary的hash方法是一個問題,我會嘗試創建一個包裝NSDictionary的類,但是在私有實現類中提供它自己的自定義hash方法 - 實現類可以在沒有警告的情況下更改(並且具有前一個) ,所以任何依賴它們的設計都很脆弱。