白话isEqual和hash的关系

出发点:isEqual和hash的关系

  • 看到这个点,上网搜一下,首先出现的就是这篇文iOS开发 之 不要告诉我你真的懂isEqual与hash!
  • 第一次看完了,确实出现了一些问号以及看了评论区的提问:isEqual和hash都重写了来得出这两者的关系?hash都有默认的实现了,为什么要重写?等等问题
  • 本文,简单的做了下梳理,补充了些个人的白话理解,更通俗易懂些

==和isEqual

  • ==

    • 基本类型,==,比较是否相等
    • 对象类型,==,只是比较对象地址即是否为同一对象
  • 默认的isEqual实现,就是self==object

    • 对于自定义类,通常需要自行去重写isEqual方法。
  • ==与isEqual的应用场景的区分在:

    • ==是比较对象地址
    • isEqual是比较对象,判等
  • 为什么要重写isEqual???

    • 看上面描述的应用场景,可以看出,如果你想比较对象地址直接用==,isEqual的最佳实践通常是比较对象,判断对象是否相等,而系统默认的isEqual实现不能满足要求。这也是为什么isEqual有比较规范的写法,有所谓的最佳实践这一说
    • 判断对象是否相等:比如以下应用场景:一个Person对象,有creditId,身份证号码属性,如果new了两个对象,都是Person类型,creditId值也相等,那我们认为这两个对象是相等的。就需要去重写isEqual来实现该场景
  • isEqual的重写思路(关心对象的内容(类型以及对象属性一致)):

    • 1、判断两指针地址,一致,返回YES;否则2
    • 2、判断指针指向对象是否为空以及对象类型是否相同。若有一个为空,或两者不是同类型对象 直接返回NO;否则3
    • 3、都不为空,且属于同类对象,而且对象的属性也相等,则返回YES
  • 当然你也可以有isEqual其他的实现,视具体应用场景

hash方法

  • 提高查找效率

  • 依然以添加person到集合类中,重写Person类的hash方法,看是否有调用到

  • hash方法调用时机:

    • hash方法只在对象被添加至NSSet和设置为NSDictionary的key时会调用
      • NSSet添加新成员,会根据hash值,快速查找成员,保证集合中是否存在该成员
      • NSDictionary在查找key时,会利用key的hash值来提高查找的效率
  • 系统默认的hash实现:

    • 直接返回对象地址(可验证的)
// 拿Person举例,有参数name,和birthday;已经重写了isEqual方法
Person *person1 = [Person personWithName:kName1 birthday:self.date1];
Person *person2 = [Person personWithName:kName1 birthday:self.date1];
NSLog(@"[person1 isEqual:person2] = %@", [person1 isEqual:person2] ? @"YES" : @"NO");	//YES,比到最后是同类对象且对象属性相等,所以对象相等

NSMutableSet *set = [NSMutableSet set];
[set addObject:person1];
[set addObject:person2];
NSLog(@"set count = %ld", set.count);	//值为2
  • 所以很明显hash方法在此处的应用,明显就是不相等的,还是系统默认实现

  • 但是,我们已经重写了,isEqual方法,期待符合判等的规则。那么在添加到set中的场景,person1和person2我们期望是要相等的

  • 重写hash方法的最佳实践,来符合我们的应用场景:

    • 如下代码的else部分:对关键属性的hash值进行位或运算作为hash值
- (NSUInteger)hash {
    BOOL isCareAddress = NO;
    NSUInteger hashValue = 0;
    if (isCareAddress) {
        // 如果期望对地址不同、但是内容相同的对象做区分
        // 结果:两个地址不同,但是内容相同的对象添加到NSMutableSet中,set.count是2
        hashValue = [super hash];
    }
    else {
        // 不关心地址是否相同,只对内容进行区分(对关键属性的hash值进行按位异或运算作为hash值)
        // 结果:两个地址不同,但是内容相同的对象添加到NSMutableSet中,set.count是1
        hashValue = [self.name hash] ^ [self.birthday hash];
    }
    return hashValue;
}

isEqual和hash方法

  • 根据上面的描述,大概应该能知道两者的关系了,注意这两者的关系并不是因为两者内部方法实现调用了彼此,纯粹在当前应用场景下,为了达到判等的最佳实践
    • hash值是对象判等的必要非充分条件
      • 如果isEqual==YES,对象相等,hash值以定向等
      • 如果两个对象hash返回相等,不一定相等,需要继续通过isEqual来判等(hash可能重复的,hash冲突)

收个尾:

  • isEqual和hash的关系

    • 在一定的场景下,hash是isEqual的必要非充分条件

      • isEqual相等,hash值相等
      • hash值相等,isEqual不一定相等,需要继续通过isEqual判等
    • 场景:对象判等

      • 当我们自定义对象,去重写isEqual的实现的时候,我们期望在进行对象判等的时候,当对象类型及属性值相等时,认为两个对象相等。区别于直接比较对象地址。(因为直接比较用==就好了,我们重写isEqual就是为了达到判等的目的,比如类似isEqualToString就是专门用来比较字符串的,是isEqual的衍生方法.还有isEqualToSet,isEqualToDictionary都是这样的场景)
      • 在NSSet添加新成员,NSDictionary作为key的hash方法的应用中,会比较两个对象的hash值来判断是否相等,系统默认直接返回对象地址,不符合对象判等的场景。通常会去重写其实现,对关键属性的hash值进行位或运算作为hash值,来达到对象判等的效果。
  • 为什么系统都已经实现了hash,我们还要去重写?

    • 因为hash算法本身就是不完美的,并不是系统的实现就是百分百对的,只是系统采用了这样的实现方法。(想想为什么会有hash冲突处理)
    • 使用hash方法是为了提高查找效率,最好设计的hash返回的值是唯一的。或者有特定的场景需求,去设计hash方法实现。这里重写,只是在判等的场景下,默认的hash实现,不是我们想要的罢了
  • 本文,加入了很多自己的理解,应该清晰了很多。欢迎交流?

  • 参考文章:

  1. iOS开发 之 不要告诉我你真的懂isEqual与hash!
  2. iOS笔记:进一步认识 ==、isEqual、hash
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值