一. ==
通常我们会用 ==
来判断两个对象是否相等,但是这样比较出来的结果可能不是我们期望的,那么 ==
究竟比较的是什么
- 对于基本类型
==
比较的是值 - 对于对象类型,
==
比较的是对象的地址,即是否为同一个对象
NSString *s1 = @"123";
NSString *s2 = [NSString stringWithFormat:@"%d", 123];
BOOL e1 = s1 == s2;
BOOL e2 = [s1 isEqual:s2];
NSLog(@"%d, %d", e1, e2);
运行结果:
0, 1
由于s1和s2不是同一个对象(即对象地址不同),所以==结果为0(NO),而isEqual方法则判断对象是否相同,所以结果为:0,1。
二.重写isEqual方法
一般我们会使用NSObject协议声明的isEqual方法来判断对象的等同性。
为了更好的进行深层次的比较,iOS系统中的NSObject子类还实现了各自的isEqual:方法。
对于自定义的类型来说,如果有判断对象是否相等需求,那么我们可以自行实现isEqual方法,具体步骤如下:
- 实现一个isEqualTo__ClassName__:方法来执行有意义的值比较.
- 重写isEqual: 方法 来作类型和对象identity检查, 回调上述的值比较方法.
- 重写 hash, 在集合中查找时最先调用(这个会在下一部分解释).
实例代码:
person.h(定义person类)
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *idStr;
@property (nonatomic, strong) NSDate *birthday;
//@property (readonly) NSUInteger hash;
@end
person.m
#import "Person.h"
@implementation Person
//区分成员变量名称和属性名称。使用@synthesize可以改变_name名称
@synthesize name = _name;
@synthesize idStr = _idStr;
- (id) initWithName:(NSString *)name idStr: (NSString *) idStr
{
if(self = [super init])
{
self.name = name;
self.idStr = idStr;
}
return self;
}
//重写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和hash
-
对象相等是相互的([a isEqual:b] ⇒ [b isEqual:a])
-
如果对象相等,它们的hash值必须相等([a isEqual:b] ⇒ [a hash] == [b hash])
-
但是,反过来不一定成立:=如果它们的hash值相等,两个对象不一定相等。([a hash] == [b hash] ¬⇒ [a isEqual:b])
-
hash方法
hash值(用于查找集合中成员的位置标识), 通过hash方法计算得来, 且hash方法返回的hash值最好唯一 -
hash方法什么时候被调用?
hash方法只在对象被添加至NSSet和设置为NSDictionary的key时会调用
NSSet添加新成员时, 需要根据hash值来快速查找成员, 以保证集合中是否已经存在该成员
NSDictionary在查找key时, 也利用了key的hash值来提高查找的效率 -
hash方法与isEqual的关系
为了优化判等的效率, 基于hash的NSSet和NSDictionary在判断成员是否相等时, 会先判断hash值是否相等,如果相等,那么就会进行isEqual的判断;反之,不相等,直接判断对象不相等
第一步: 集成成员的hash值是否和目标hash值相等, 如果相同进入Step 2, 如果不等, 直接判断不相等
第二步: hash值相同(即Step 1)的情况下, 再进行对象判等, 作为判等的结果
简单地说就是hash值是对象判等的必要非充分条件 -
如何重写hash方法
对于上面Person类的hash方法实现如下
- (NSUInteger)hash {
return [self.name hash] ^ [self.birthday hash];
}