iOS - 判断对象相等,重写isEqual,hash

一. ==

通常我们会用 == 来判断两个对象是否相等,但是这样比较出来的结果可能不是我们期望的,那么 == 究竟比较的是什么

  • 对于基本类型==比较的是值
  • 对于对象类型,==比较的是对象的地址,即是否为同一个对象
	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方法,具体步骤如下:

  1. 实现一个isEqualTo__ClassName__:方法来执行有意义的值比较.
  2. 重写isEqual: 方法 来作类型和对象identity检查, 回调上述的值比较方法.
  3. 重写 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

  1. 对象相等是相互的([a isEqual:b] ⇒ [b isEqual:a])

  2. 如果对象相等,它们的hash值必须相等([a isEqual:b] ⇒ [a hash] == [b hash])

  3. 但是,反过来不一定成立:=如果它们的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];
}

参考
参考文章2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值