NCZZSK

自在生活

  • 主页
所有文章 true

NCZZSK

自在生活

  • 主页

白话isEqual和hash的关系

2019-09-08

出发点: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实现:

    • 直接返回对象地址(可验证的)
1
2
3
4
5
6
7
8
9
// 拿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值
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      - (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
赏

晚饭加鸡腿🍗

支付宝

扫一扫,分享到微信

微信分享二维码
一道关于老鼠繁衍的算法题
CocoaPods深入一点
© 2019 NCZZSK
Hexo Theme Yilia by Litten
  • 所有文章
  • true

tag:

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

一个人如果没有梦想
跟无忧无虑有什么区别呢?

如果你笑了
那就太好啦~