2013-02-04 56 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方法 - 实现类可以在没有警告的情况下更改(并且具有前一个) ,所以任何依赖它们的设计都很脆弱。