iOS 一个对象的等同性

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 容器中最好不要放入可变类型的对象

posted @ 2020-06-27 10:39  wjwdive  阅读(272)  评论(0编辑  收藏  举报