为什么要有isEqual方法?
isEqual方法的作用大家肯定是知道的:
判断两个对象是否相等
但是判断相等不是已经有==运算符了么, 为什么还要isEqual方法?
这是因为:
对于基本类型, ==运算符比较的是值; 对于对象类型, ==运算符比较的是对象的地址(即是否为同一对象)
注意: 上述==运算符的说明适用于Objective-C和Java等不支持运算符重载的语言, 支持运算符重载的语言有C++
所以要理清==运算符和isEqual方法的区别, 问题就集中在
什么叫比较对象的地址, 什么叫比较对象
我们通过下面的例子来说明这个问题
UIColor *color1 = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
UIColor *color2 = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
NSLog(@"color1 == color2 = %@", color1 == color2 ? @"YES" : @"NO");
NSLog(@"[color1 isEqual:color2] = %@", [color1 isEqual:color2] ? @"YES" : @"NO");
打印结果如下
color1 == color2 = NO
[color1 isEqual:color2] = YES
从上面的例子可以看出, ==运算符只是简单地判断是否是同一个对象, 而isEqual方法可以判断对象是否相同, 例如UIColor对象表示的color是否相同
如何重写自己的isEqual方法?
对于Cocoa Framework中定义的类型, 例如上面例子中的UIColor, isEqual方法已经实现好了
常见类型的isEqual方法还有NSString isEqualToString / NSDate isEqualToDate / NSArray isEqualToArray / NSDictionary isEqualToDictionary / NSSet isEqualToSet, 更多参考[Equality](https://link.jianshu.com?t=http://nshipster.com/equality/)
但对于自定义类型来说, 通常需要重写isEqual方法
通过下面的例子, 我们来看看重写isEqual方法的正确姿势
首先定义Person类如下
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSDate *birthday;
@end
Person类中实现的isEqual方法如下
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[Person class]]) {
return NO;
}
return [self isEqualToPerson:(Person *)object];
}
- (BOOL)isEqualToPerson:(Person *)person {
if (!person) {
return NO;
}
BOOL haveEqualNames = (!self.name && !person.name) || [self.name isEqualToString:person.name];
BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday];
return haveEqualNames && haveEqualBirthdays;
}
上述代码主要步骤如下
Step 1: ==运算符判断是否是同一对象, 因为同一对象必然完全相同
Step 2: 判断是否是同一类型, 这样不仅可以提高判等的效率, 还可以避免隐式类型转换带来的潜在风险
Step 3: 通过封装的isEqualToPerson方法, 提高代码复用性
Step 4: 判断person是否是nil, 做参数有效性检查
Step 5: 对各个属性分别使用默认判等方法进行判断
Step 6: 返回所有属性判等的与结果
isEqual的实现并不复杂, 但是从代码质量(效率, 安全, 复用)来说, 上述实现仍然值得仔细学习和借鉴
除了上面的最佳实践, 还有一种最不佳实践
@implementation NSDate (Approximate)
- (BOOL)isEqual:(id)object {
return YES;
}
@end
这里的isEqual方法一直返回YES
NSLog(@"[self.date1 isEqual:@\"hello\"] = %@", [self.date1 isEqual:@"hello"] ? @"YES" : @"NO");
打印结果如下
[self.date1 isEqual:@"hello"] = YES
这个有趣的实验说明: 对象的判等可以完全由您决定, 即使两个完全不同的对象