读EffectiveObjective-C2.0(第八条)

2020-11-11  本文已影响0人  LazyLoad

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

NSString *foo = @"Badger 123";
NSString *bar = [NSString stringWithFormat:@"Badger %i", 123];
NSLog(@"%d", foo == bar);       // false
NSLog(@"%d", [foo isEqual:bar]); // true
NSLog(@"%d", [foo isEqualToString:bar]); // true

从上述代码可以看出==和等同性判断的差别。NSString类实现了一个自己独有的等同性判断的方法,必须传递一个字符串对象作为参数。比isEqual:方法速度快,而isEqual:方法还要执行额外的步骤。因为它不知道接收参数的对象类型。

- (BOOL) isEqual: (id)anObject
{
  return (self == anObject);
}

- (NSUInteger) hash
{
  /*
   *  malloc() must return pointers aligned to point to any data type
   */
#define MAXALIGN (__alignof__(_Complex long double))

  static int shift = MAXALIGN==16 ? 4 : (MAXALIGN==8 ? 3 : 2);

  /* We shift left to lose any zero bits produced by the
   * alignment of the object in memory.
   */
  return (NSUInteger)((uintptr_t)self >> shift);
}
// EOCPerson.h
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, assign) NSUInteger age;
@end
  
// EOCPerson.m
- (BOOL)isEqual:(id)object {
    if (self == object) { // 同一个对象返回YES
        return YES;
    }
    
    if ([self class] != [object class]) { // 不是同一个类型,返回NO
        return NO;
    }
    
    EOCPerson *otherPerson = (EOCPerson *)(object);
    if (![_firstName isEqualToString:otherPerson.firstName]) { // 名字不一样,返回NO
        return NO;
    }
    
    if (![_lastName isEqualToString:otherPerson.lastName]) { // 姓不一样,返回NO
        return NO;
    }
    
    if (_age != otherPerson.age) { // 年龄不一样,返回NO
        return NO;
    }
    return YES;
}
- (NSUInteger)hash {
    return 1337;
}

如果这么写,在collection(代表Array、Dictionary、Set等数据结构)中使用会产生性能问题,因为collection在检索哈希表时,会用对象的哈希值作为索引。加入某个collection是用set实现的,那么set可能会根据哈希值把对象分装到不同数组,在向set添加对象的时候,要根据哈希值找到与之相关的数组,依次检查其中的各个元素,看数组中已有的对象是否将要添加的新对象相等。如果相等说明对象已经在set里面了。如果对象都返回相同的哈希值,那么在set中已有100000个对象的情况下,若是继续向其中添加新对象,最坏情况下,需要把100000个对象都遍历一遍。

- (NSUInteger)hash {
    NSString *stringToHash = [NSString stringWithFormat:@"%@:%@:%i", _firstName, _lastName, _age];
    return [stringToHash hash];
}
- (NSUInteger)hash {
    NSUInteger firstNameHash = [_firstName hash];
    NSUInteger lastNameHash = [_lastName hash];
    NSUInteger ageHash =_age;
    return firstNameHash ^ lastNameHash ^ ageHash;
}

特定类所具有的等同性判定方法

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

- (BOOL)isEqual:(id)object {
    if ([self class] == [object class]) {
        return [self isEqualToPerson:(EOCPerson *)object];
    } else {
        return [super isEqual:object];
    }
}

等同性判定的执行深度

容器中可变类的等同性

NSMutableSet *set = [NSMutableSet set];
NSMutableArray *arrayA = @[@1, @2].mutableCopy;
[set addObject:arrayA];
NSLog(@"set = %@", set);
/* set = {(
(1,2)
)}
*/
NSMutableArray *arrayB = @[@1, @2].mutableCopy;
[set addObject:arrayB];
NSLog(@"set = %@", set);
/* set = {(
(1,2)
)}
*/
NSMutableArray *arrayC = @[@1].mutableCopy;
[set addObject:arrayC];
NSLog(@"set = %@", set);
/*
set = {(
        (1),
        (1,2)
)}
*/
[arrayC addObject:@2];
NSLog(@"set = %@", set);
/*
set = {(
        (1,2),
        (1,2)
)}
*/
NSSet *setB = [set copy];
NSLog(@"setB = %@", setB);
/*
setB = {(
        (1,2)
)}
*/
上一篇下一篇

猜你喜欢

热点阅读