OC判断对象是否相等

你是否真的了解OC对象相等?

标签: objective-c

比较对象

  比较两个对象是否相等是一个常用的功能。==操作符比较的不是对象,而是两个指针本身,一般来说这不是咱们所想要的。要想判断对象是否相等,应该使用 NSObject 协议中声明的isEqual:方法来判断。一般来说,两个类型不同的对象总是不相等的。
  如果你已经有过一些 OC 的编码经验的话,你一定自定义过isEqual:方法。那么你是碰到过一些奇葩的问题呢?咱们先来看一段代码:

@interface Person : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@end

@implementation Person

- (BOOL)isEqual:(id)object {
  if (self == object) return YES;
  if (![object isKindOfClass:[Person class]]) return NO;

  return [self.name isEqualToString:[object name]];
}

- (id)copyWithZone:(NSZone *)zone {
  Person *person = [[Person allocWithZone:zone] init];
  person.name = self.name;
  return person;
}
@end

- (void)test {
  Person *person = [[Person alloc] init];
  person.name = @"Lili";

  NSDictionary *dic = @{person: @1};
  Person *personCopy = [person copy];

  id object = [dic objectForKey:personCopy];
  NSLog(@"%@", [person isEqual:personCopy] ? @"YES" : @"NO"); // 输出:YES
  NSLog(@"%@", object);  // 输出:(null)
}

  这段代码定义了类:Person,实现了isEqual:方法,并实现了NSCopying协议。在测试中,以 Person 对象作为 key放到了字典中。再以 person 对象 copy 出一个新的对象:personCopy。但用这个 personCopy 作为 key 从字典中去取相应的值时,居然没有取到,输出为(null)。但咱们判断过 person 与 personCopy 对象呀:[person isEqual:personCopy],而且它是的确是相等的。
  那么问题出在哪了?系统问题?不可能。咱们先看看NSDictionary中对key的要求:

  在字典中,keys都是唯一的。也就是说,在一个字典中不会有两个key相等(通过isEqual:来判断)。一般来说,一个key可以是任何实现了NSCopying协议的对象

  NSCopying协议只定义了copyWithZone:方法,咱们也实现了,不是这个问题。若非问题出在isEqual:上?这个方法声明在NSObject协议中。来看看这个方法其中一段说明:

  如果两个对象是相等的,那么它们就必须有相同的哈希码(hash value)。如果你的自定义类型定义了isEqaul:方法,并打算将其实例需要放到集合中,这点尤为重要。你应该确保在定义了isEqual:方法的同时,也定义hash方法。

  也就是说,NSObject协议中有两个用于判断等同性的方法:

  - (BOOL)isEqual:(id)object;   - (NSUInteger)hash;

  NSObject类对这两个方法的默认实现是:当且仅当其“指针值”完全相等时,这两个对象才相等。若想在自定义类中正确实现这些方法,就必须先理解其约定。如果isEqual:方法判定两个对象相等,那么其hash方法也必须返回同一值。但是,如果两个对象的hash方法返回同一个值,那么isEqual:方法未必会认为两者相等。

  哦,原来是这样呀,看来问题就是出在这里了。那么咱们写一个hash方法出来看看:

// Person.m
- (NSUInteger)hash {
  return [self.name hash];
}

// Test
- (void)test {
  Person *person = [[Person alloc] init];
  person.name = @"Lili";

  NSDictionary *dic = @{person: @1};
  Person *personCopy = [person copy];

  id object = [dic objectForKey:personCopy];
  NSLog(@"%@", [person isEqual:personCopy] ? @"YES" : @"NO"); // 输出:YES
  NSLog(@"%@", object);  // 输出:1
}

  OK,这回没有问题了。
  
  结论:实现isEqual:方法的同时,要实现hash方法!
 
  

hash 要注意的地方

  在集合中(如 NSDictionary 或 set)会使用对象的哈希码来决定对象所存储的位置。 如果一个可变对象(mutable object)加到这样一个集合中的后,其hash方法所返回的哈希码必须保持一致,不能改变。如下面代码 所示:

- (void)test {
  Person *person = [[Person alloc] init];
  person.name = @"Lili";

  NSMutableDictionary *dic = [@{person: @1} mutableCopy];
  NSLog(@"%@", dic); // 输出:{ Lili = 1; }

  Person *personOther = [[Person alloc] init];
  personOther.name = @"jany";
  dic[personOther] = @2;
  NSLog(@"%@", dic); // 输出:{ Lili = 1; jany = 2; }

  Person *personSKey = [dic allKeys][1];
  personSKey.name = @"Lili";
  NSLog(@"%@", dic); // 输出:{ Lili = 1; Lili = 1; } 错误!
}

  上面的字典dic被整得乱套了有木有~ 完离这种写法,珍爱生命!

你可能感兴趣的:(oc)