2

This SO answerは、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 に答える