OC开发中我们常常需要比较两个对象是否相等,但是依靠==操作符进行比较的结果未必使我们想要的。因为实例对象本质上是指针,
使用==符号实际上是比较两个指针指向内容本身是否相等,而不是比较指向对象
。没有操作符重载的OC在基类NSObject协议中声明了方法isEqual和hash来比较对象,通过代码来演示isEqual的作用:
上面三个方法可以比较出==跟isEqual方法的区别,而除了这两个比较外还有一个isEqualToString方法,它跟isEqual有什么差别?
isEqualToString接收一个NSString的参数,用于判断两个字符串变量的值是否相等 。如果传递给该方法的参数不是NSString类型,那么程序将会crash。对比isEqual方法,前者执行速度要比后者快,但是后者对于参数类型没有限制。另外,isEqualToString方法也是必须要创建的,这点会在下面进行讲解。
接下来就是hash函数,hash返回被测对象的哈希值:
两个对象也返回了同一个hash值,那么是否这意味着我们可以使用isEqual和hash两个方法任意一个来判断两个对象是否相等呢?答案是否。我们来推测一下isEqual的内部实现。因为OC对NSObject进行了封装,我们无法看到其实现文件,但能从功能上推测出来,我们定义一个Person类重写该方法:
我们可以看到,在otherPerson对象内部值改变之后,isEqual就返回了NO,说明了我们的重写是正确的。但是hash值在变量值发生变化之后依旧没有任何改变,这说明了 我们无法通过hash值来判断两个对象是否相等 。
我们再来看看对isEqual重写的函数,先判断两个指针是否指向同一块内存,如果是就返回正确。第二个判断是判断两个对象类型是否相同, 在继承体系中,我们必须注意对比两个对象时,确保它们的类型是一致的 。接着就对比内部成员的值,全部正确就返回比较成功。看起来这个方法非常成功,但是, 这样的重写是错误的 !
在苹果官方规定中有提及: 如果isEqual方法判定两个对象相等,那么其hash方法也必须返回同一个值 。而上面的代码运行中我们可以看到两个对象的哈希值是不一样的,而isEqual方法却返回了正确,这也说明了默认的hash值是以对象地址为原型进行算法修改的! 而hash想要返回同样的值,意味着两个实例指针必须指向同一个对象或者我们重写hash方法 。但是 重写hash方法需要使用计算速度快而且哈希码碰撞概率低的算法 ,非常的麻烦。所以正常情况下我们都会改写isEqual方法,而这就说明了我们重写的isEqual函数中,从第一个判断开始,之后的全部要去掉。去掉的同时也说明了我们无法通过isEqual方法来对比两个同类型的不同对象的大小,所以这时候我们需要重新声明一个函数来判断,这就是为什么会出现上文中的isEqualToString方法。那么我们再次进行重写:
我们在自定义类时一定要记住对象等同性的规则,重写isEqual方法和响应的isEqualTo...方法有助于我们写出更健壮的代码。当然了,在容器中可变长类使用对象等同性时,可能造成意想不到的数据错误,这点在以后再细讲。
上面三个方法可以比较出==跟isEqual方法的区别,而除了这两个比较外还有一个isEqualToString方法,它跟isEqual有什么差别?
isEqualToString接收一个NSString的参数,用于判断两个字符串变量的值是否相等 。如果传递给该方法的参数不是NSString类型,那么程序将会crash。对比isEqual方法,前者执行速度要比后者快,但是后者对于参数类型没有限制。另外,isEqualToString方法也是必须要创建的,这点会在下面进行讲解。
接下来就是hash函数,hash返回被测对象的哈希值:
两个对象也返回了同一个hash值,那么是否这意味着我们可以使用isEqual和hash两个方法任意一个来判断两个对象是否相等呢?答案是否。我们来推测一下isEqual的内部实现。因为OC对NSObject进行了封装,我们无法看到其实现文件,但能从功能上推测出来,我们定义一个Person类重写该方法:
我们可以看到,在otherPerson对象内部值改变之后,isEqual就返回了NO,说明了我们的重写是正确的。但是hash值在变量值发生变化之后依旧没有任何改变,这说明了 我们无法通过hash值来判断两个对象是否相等 。
我们再来看看对isEqual重写的函数,先判断两个指针是否指向同一块内存,如果是就返回正确。第二个判断是判断两个对象类型是否相同, 在继承体系中,我们必须注意对比两个对象时,确保它们的类型是一致的 。接着就对比内部成员的值,全部正确就返回比较成功。看起来这个方法非常成功,但是, 这样的重写是错误的 !
在苹果官方规定中有提及: 如果isEqual方法判定两个对象相等,那么其hash方法也必须返回同一个值 。而上面的代码运行中我们可以看到两个对象的哈希值是不一样的,而isEqual方法却返回了正确,这也说明了默认的hash值是以对象地址为原型进行算法修改的! 而hash想要返回同样的值,意味着两个实例指针必须指向同一个对象或者我们重写hash方法 。但是 重写hash方法需要使用计算速度快而且哈希码碰撞概率低的算法 ,非常的麻烦。所以正常情况下我们都会改写isEqual方法,而这就说明了我们重写的isEqual函数中,从第一个判断开始,之后的全部要去掉。去掉的同时也说明了我们无法通过isEqual方法来对比两个同类型的不同对象的大小,所以这时候我们需要重新声明一个函数来判断,这就是为什么会出现上文中的isEqualToString方法。那么我们再次进行重写:
我们在自定义类时一定要记住对象等同性的规则,重写isEqual方法和响应的isEqualTo...方法有助于我们写出更健壮的代码。当然了,在容器中可变长类使用对象等同性时,可能造成意想不到的数据错误,这点在以后再细讲。