1、认识NSString中==、- (BOOL)isEqual:(id)object;,- (NSInteger)hash;, - (BOOL)isEqualToString:(NSString *)otherString;一个运算符和三个函数。
1、== 是比较两个指针本身是否相同,而不是指针所指的对象。
2、NSObject协议中isEqual:, hash方法的默认实现都是当且仅当“指针值”完全相等时,这两个对象才相等。
对象的“等同性”:如果两个对象比较,isEqual:方法返回YES,两个对象的hash值必定相等,但是二者的hash值相等,isEqual:函数的返回值不一定是YES,也即两个hash值相同的对象未必相等
自定义类的实现
@interface Person: NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation
/*
直接判断两个指针是否是同一个,一样的话,肯定所指的对象必是同一个。
否则,比较两个对象所属的类,若不是同一个类,则二者必不相等。其实,不严格来讲,如果一个父类和一个子类的实例比较可以相等,另当别论,因为在继承体系中确实会遇到此问题。
接下来,比较各个属性值是否相等。
*/
- (BOOL)isEqual:(id)object{
if(self == object) return YES;
if([self class] != [object class]) return NO;
Person *otherPerson = (Person *)object;
if(![_firstName isEqualToString: otherPerson.firstName]) return NO;
if(![_lastName isEqualToString: otherPerson.lastName]) return NO;
if(_age != otherPerson.age) return NO;
return YES;
}
/*可行但是不好的一种方法:
这种写法会有性能问题,试想,一个集合类collection(NSArray或者NSSet,NSDictionary)中有多个该类型的对象,collection在检索哈希表(hash table)时,会用对象的哈希码作为索引。假如某个collection是用set实现的,那么set可能会根据哈希吗把对象分装到不同的数组中。 在向set中添加新对象时,要根据其hash码找到与之相关联的数组,一次检查其中各个元素,看数组中已有的对象是否和将要添加的新对象相等。如果相等,那么就说明要添加的对象已经在set里了。由此可见,如果每个对象都返回相同的hash码,那么在set中已有1000000个对象的情况下,若是继续向其中添加对象,则需要将这1000000个对象全部扫描一遍。
*/
- (NSUinteger)hash {
return 1337;
}
/*
另一种实现
将Person对象中的属性都塞入一个字符串中,然后用hash方法返回该字符串的哈希码。这样就符合“等同性”约定。因为两个相等的Person总会返回相等的哈希码。但是这种方法要创建字符串,拼接并生产hash码,开销显然比第一种直接返回一个单一数值大。把这种哈希实现的对象添加到collection时也会产生性能问题,因为要添加必须先计算一遍hash。
*/
- (NSUinteger)hash {
NSString *stringToHash = [NSSting stringWithFormat:@"%@:%@:%i",_firstName, lastName, _age];
return [stringToHash hash];
}
/*
最适合的一种实现
这种做法既能保持一定的高效率,又能时生成的hash至少位于一定范围之内,而不会过于频繁地重复。
*/
- (NSUinteger)hash {
NSUinteger firstNameHash = [_firstName hash];
NSUinteger lastNameHash = [_lastName hash];
NSUinteger ageHash = _age;
return firstNameHash = firstNameHash^lastNameHash^ageHash;
}
/*
在自定义的方法中,要实现isEqualToPerson 和 isEqual:方法
*/
- (BOOL)isEqualToPerson:(Person *)otherPerson {
if(self == otherPerson) return YES;
if(![_firstName isEqualToString:otherPerson.firstName]) return NO;
if(![_lastName isEqualToString:otherPerson.lastName]) return NO;
if(_age != otherPerson.age) return NO;
return YES;
}
- (BOOL)isEqual:(id)object {
if([self class] == [object class]) {
return [self isEqualToPerson:(Person *)object;
} else {
return [super isEqual:object];
}
}
@end
hash碰撞时不可能避免的,我们编写该方法时要注意减少碰撞和降低计算hash的复杂度。
如果一个Person 对象是根据数据库里的数据创建的,那么数据库中肯定有主键,由于主键是唯一标识符,我们这时就可以根据该唯一标识符来判断等同性
collection 容器中最好不要放入可变类型的对象