2

这个SO 答案表明 NSDictionary 的哈希是字典中的条目数。(类似地,NSArray 的散列是它的长度。)答案继续建议创建一个类别以提供更好的散列实现。

如果您需要更准确的哈希值,您可以在 Obj-C 类别中自己提供一个。

但是当我尝试这个时,它似乎仍然使用原始的哈希实现。

我们有标题NSDictionary+Hash.h

#import <Foundation/Foundation.h>

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

并在NSDictionary+Hash.m

#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'”。

现在,如果我在类别头文件和实现文件中将方法从 hash 重命名为 hashy(例如),则[dict hashy]返回正确的值。不能覆盖类别中的“内置”方法吗?我做错了什么吗?

4

1 回答 1

10

NSDictionary 是一个类集群——当你向一个 NSDictionary 发送消息时,你与之交互的从来不是一个实际的 NSDictionary 实例,而是一个私有子类的实例。所以当你重写hash你的类中的方法时,它确实是重写了 NSDictionary 中的那个方法,但是具体的子类有它自己的hash方法,所以它重写了你的。

如果你真的想这样做,我想你会想要检查 NSDictionaryI 和 NSDictionaryM 类的存在并动态覆盖它们的hash方法。但这在内部实现细节上很混乱,除非你真的陷入困境,否则我不建议这样做。如果您分析并发现 NSDictionary 的hash方法有问题,我会尝试创建一个包装 NSDictionary 但提供自己的自定义hash方法的类,然后再使用私有实现类 - 实现类可以在没有警告的情况下更改(并且之前有),所以任何依赖它们的设计都是脆弱的。

于 2013-02-04T22:47:07.667 回答