出发点: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方法只在对象被添加至NSSet和设置为NSDictionary的key时会调用
-
系统默认的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冲突)
- 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实现,不是我们想要的罢了
-
本文,加入了很多自己的理解,应该清晰了很多。欢迎交流?
-
参考文章: