重写isEqual的最佳实践:和散列
  你如何正确地重写isEqual:在Objective-C中?  “catch”似乎是,如果两个对象相等(由isEqual:方法确定),它们必须具有相同的散列值。 
  Cocoa Fundamentals Guide的Introspection部分提供了一个关于如何覆盖isEqual:的例子isEqual:复制如下,名为MyWidget的类: 
- (BOOL)isEqual:(id)other {
    if (other == self)
        return YES;
    if (!other || ![other isKindOfClass:[self class]])
        return NO;
    return [self isEqualToWidget:other];
}
- (BOOL)isEqualToWidget:(MyWidget *)aWidget {
    if (self == aWidget)
        return YES;
    if (![(id)[self name] isEqual:[aWidget name]])
        return NO;
    if (![[self data] isEqualToData:[aWidget data]])
        return NO;
    return YES;
}
  它检查指针是否相等,然后检查类是否相等,最后使用isEqualToWidget:比较对象,它只检查name和data属性。  该示例没有显示的是如何重写hash 。 
  假设有其他属性不影响平等,比如age 。  不应该重写hash方法,只有name和data会影响哈希?  如果是这样,你会怎么做?  只需添加name和data的哈希值?  例如: 
- (NSUInteger)hash {
    NSUInteger hash = 0;
    hash += [[self name] hash];
    hash += [[self data] hash];
    return hash;
}
  这足够吗?  有更好的技术吗?  如果你有像int这样的基元呢?  将它们转换为NSNumber以获得它们的散列?  或像NSRect结构? 
  ( Brain fart :最初写了“按位或”,它们和|=一起|= 。 
从...开始
 NSUInteger prime = 31;
 NSUInteger result = 1;
然后为你做的每一个原始
 result = prime * result + var
对于64位,你可能也想换个xor。
 result = prime * result + (int) (var ^ (var >>> 32));
对于你使用0的对象为零,否则使用它们的哈希码。
 result = prime * result + [var hash];
对于布尔值,你使用两个不同的值
 result = prime * result + (var)?1231:1237;
解释和归属
这不是tcurdt的工作,并且评论需要更多解释,所以我相信对归因的编辑是公平的。
该算法在“Effective Java”一书中得到了推广,相关章节目前可在线查找。 该书推广了该算法,该算法现在是许多Java应用程序(包括Eclipse)中的默认算法。 然而,它来自一个更老的实现,这个实现不同地归因于Dan Bernstein或Chris Torek。 这个较旧的算法最初是围绕Usenet浮现的,而某些归因则很困难。 例如,这个Apache代码中有一些有趣的评论(搜索他们的名字)引用了原始源代码。
底线是,这是一个非常古老的简单哈希算法。 它不是最高性能的算法,它甚至不是数学上证明是“好”的算法。 但这很简单,很多人用了很长时间,效果很好,所以它有很多历史的支持。
我只是自己拾取Objective-C,所以我不能专门为那种语言说话,但在其他语言中,如果两个实例是“相等”,他们必须返回相同的散列 - 否则您将拥有全部尝试将它们用作散列表(或任何字典类型集合)中的键时存在各种各样的问题。
另一方面,如果两个实例不相同,它们可能会或可能不会具有相同的哈希值 - 最好不要。 这是在散列表上进行O(1)搜索和O(N)搜索之间的区别 - 如果所有散列相撞,则可能发现搜索表并不比搜索列表更好。
就最佳实践而言,您的哈希应该为其输入返回值的随机分布。 这意味着,例如,如果您有双精度值,但大多数值趋于在0到100之间进行聚类,则需要确保这些值返回的散列均匀分布在整个可能的散列值范围内。 这将显着提高你的表现。
这里有许多哈希算法,包括这里列出的几个哈希算法。 我尽量避免创建新的散列算法,因为它可能会有很大的性能影响,所以使用现有的散列方法并按照您的示例进行某种按位组合,是避免它的好方法。
  我发现这个线程非常有帮助,提供了我需要的所有东西来获得我的isEqual:和一个捕获实现的hash方法。  在isEqual:测试对象实例变量时isEqual:示例代码使用: 
if (![(id)[self name] isEqual:[aWidget name]])
    return NO;
  在我的单元测试中,当我知道对象是相同的时候,这个错误(即返回NO)没有错误。  原因是,其中一个NSString实例变量为零,所以上面的语句是: 
if (![nil isEqual: nil])
    return NO;
并且由于零将回应任何方法,这是完全合法的,但是
[nil isEqual: nil]
  返回nil,这是NO,所以当对象和正在测试的对象都有一个零对象时,它们将被认为是不相等的(即, isEqual:将返回NO)。 
这个简单的解决方法是将if语句更改为:
if ([self name] != [aWidget name] && ![(id)[self name] isEqual:[aWidget name]])
    return NO;
这样,如果他们的地址是相同的,则跳过方法调用,无论它们是否都是零或者都指向同一个对象,但是如果其中任一个不是零,或者它们指向不同的对象,则适当地调用比较器。
我希望这可以帮助人们在几分钟内搔抓。
链接地址: http://www.djcxy.com/p/58481.html