第 8 条:理解“对象等同性”这一概念

== 操作符比较的是两个指针本身,而不是其所指的对象。应使用 NSObject 协议中声明的 isEqual: 方法来判断两个对象的等同性。

例如:

NSString *foo = @"Badger 123";
NSString *bar = [NSString stringWithFormat:@"Badger %i", 123];
    
BOOL equalA = (foo == bar); //< equalA = NO
BOOL equalB = [foo isEqual:bar]; //< equalB = YES
BOOL equalC = [foo isEqualToString:bar]; //< equalC = YES

NSObject 协议中有两个判断等同性的关键方法:

- (BOOL)isEqual:(id)object;
+ (NSUInteger)hash;
// 这两个方法的默认实现是:当且仅当其“指针值”(pointer value)完全相等时,两个对象才相等。

注意:若 isEqual: 方法判定两个对象相等,那么其 hash 方法也必须返回同一个值;若两个对象的 hash 方法返回同一个值,那么 isEqual: 未必认为二者相等。

hash: 方法实现

/* 方法一 */
- (NSUInteger)hash {
    return 1337;
}
/* 方法二 */
- (NSUInteger)hash {
    NSString *stringToHash = [NSString stringWithFormat:@"%@:%@", _firstName, _lastName];
    return [stringToHash hash];
}
/* 方法三 */
- (NSUInteger)hash {
    NSUInteger firstNameHash = [_firstName hash];
    NSUInteger lastNameHash = [_lastName hash];
    return firstNameHash ^ lastNameHash; //按位异或
}

三种方法对比:
方法一:会产生性能问题。
方法二:创建字符串开销,添加到collection也会有性能问题。
方法三:高效,哈希码在一定范围内,会碰撞。

特定类的等同性判断方法

NSString: isEqualToString:
NSArray: isEqualToArray:
NSDictionary: isEqualToDictionary:

自定义判断等同性方法:

- (BOOL)isEqualToPerson:(ViewController*)otherPerson {
    if (self == otherPerson) return YES;
    
    if (![_firstName isEqualToString:otherPerson.firstName]) {
        return NO;
    }
    if (![_lastName isEqualToString:otherPerson.lastName]) {
        return NO;
    }
    return YES;
}

// 重写“isEqual:”方法
- (BOOL)isEqual:(id)object {
    if ([self class] == [object class]) {
        return [self isEqualToPerson:(ViewController*)object];
    } else {
        return [super isEqual:object];
    }
}

等同性判定执行深度

有时无需将所有数据逐个比较,只根据其中一部分数据即可判断二者是否等同。

容器中可变类的等同性

把某个对象放入 collection 之后,就不应再改变其哈希码了。

要点:

  • 相同的对象必须有相同的哈希码,但哈希码相同的对象未必相同。
  • 不要盲目地逐个检测每条属性,应依照具体需求来检测。
  • 编写 hash 方法时,应使用计算速度快且哈希码碰撞几率低的算法。

主要来源:《Effective Objective-C 2.0》

你可能感兴趣的:(第 8 条:理解“对象等同性”这一概念)