两个对象是否相等是开发中经常遇到的问题,如果使用==
判断两个对象是否指向同一个地址并不一定能满足有所有需求,两个对象相等的判定可能是其”uniqueId”是一致的。所以应该使用NSObject协议中声明的isEqual
方法。
一般来说,即便创建了两个所有属性值都相同的对象,使用isEqual
方法判定两个对象也依然不是等同的,因为isEqual
的默认实现是当且仅当两个对象的指针值完全相等。若想自定义两个对象是否等同,需要制定一些规则,在何种方式下两个对象是等同的。
NSObject定义了两个用于判断对象是否相等的方法:
1 2
| - (BOOL)isEqual:(id)object; - (NSUInteger)hash;
|
如果isEqual
判定两个对象相等,那么其hash值也必须返回同一个值。hash的默认实现方法类似于这样:
1 2 3
| - (NSUInteger)hash { return (NSUInteger)self; }
|
比如一个类是这样定义的:
1 2 3 4 5 6 7 8 9
| @interface : NSObject { char *_data; int _uid; NSMutableDictionary *_ext; } @property (nonatomic,copy)NSString *userName; @property (nonatomic,copy)NSString *passWord; @end
|
那么其isEqual方法可以这样实现:
1 3 4 5 6
| - (BOOL)isEqual:(UserEntity *)object { return ([object isKindOfClass: [UserEntity class]] && [object.userName isEqual:self.userName] && [object.passWord isEqual:self.userName] && object->_uid == self->_uid); }
|
在hash方法中可以这样实现:
1 2 3
| - (NSUInteger)hash { return [_userName hash]^[_passWord hash]^_uid; }
|
需要注意的是,该hash算法可能会产生一定的碰撞,也就是说不保证一定会生成一个唯一的哈希码,这里我们可以参考这篇文章的实现:implementing-equality-and-hashing
1 2 3 4 5 6
| #define NSUINTROTATE(val, howmuch) ((((NSUInteger)val) << howmuch) | (((NSUInteger)val) >> (NSUINT_BIT - howmuch))) - (NSUInteger)hash { return NSUINTROTATE([_userName hash], NSUINT_BIT / 2) ^ [_passWord hash]^_uid; }
|
另外还需要注意的是,如果我们使用了继承子类和父类做比较,那么UserEntity
类的isEqual方法可能需要判断两个对象是否是父子关系,而不是仅仅判断是否是同一个类。
在一些容器类中,会使用isEqual方法判断两个对象是否相等,比如NSArray的- (NSUInteger)indexOfObject:(ObjectType)anObject;
,NSMutableSet类的- addObject
方法。如果在使用时可变容器时,我们修改了已经存在于容器中的元素的值导致了该元素与其他元素的hash值相同,那么将会造成一些无法预知的隐性错误。