Objective-C中“对象等同性”的判定方法
2016-01-17 19:59
671 查看
Objective-C中“对象等同性”的判定方法
根据“等同性”来比较对象是一个非常有用的功能。不过通过==操作符比较出来的结果未必是我们想要的,因为该操作比较的是两个指针本身,而不是其所指的对象。应该使用NSObject协议中声明的“isEqual:”方法来判定两个对象的等同性。一般来说,两个类型不同的对象总是不相等的。某些对象对象提供了特殊的“等同性判定方法”,如果已经知道两个受测对象都属于同一个类,那么就可以使用这种方法。以下述代码为例:
可以看到==与等同性判定方法之间的差别。NSString类实现了一个自己所独有的等同性判定方法,名为“isEqualToString:”。传递给该方法的对象必须是NSString,否则结果为未定义。调用该方法比调用isEqual:方法快,后者还要执行额外的步骤,因为要判断受测对象的类型。
NSObject协议中有两个用于判定等同性的关键方法:
NSObject类对这两个方法的默认实现是:当且仅当“指针指”完全相等时,这两个对象才相等。若想在自定义的对象中正确覆写这些方法,就必须先理解其中的约定。如果“isEqual:”方法判定两个对象相等,那么其hash方法必须返回同一个值。但是,如果两个对象的hash方法返回同一个值,那么“isEqual:”方法未必会认为两者相等。
比如有下面这个类:
一般认为,如果两个EOCPerson的所有字段均相等,那么这两个对象就相等。于是“isEqual:”方法就可以写成:
首先,直接判断两个指针是否相等。若相等,则其均指向同一个对象,所以受测的对象也必定相等。
比较两个类所属的类。若不属于同一个类,则两个对象不相等。不过有时我们可能认为:一个父类实例可以与其子类实例相等。在继承体系中判断等同性时,经常遭遇到此类问题。所以实现“isEqual:”方法时要考虑到这种情况。
检测每个属性是否相等。只要其中有不相等的属性,就判定这两个对象不相等,否则两个对象相等。
实现hash方法。根据等同性约定:若两个对象相等,则其哈希码也相等,但是两个哈希码相等的对象却未必相等。这是能否正确覆写“isEqual:”方法的关键所在。
哈希方法的实现:
返回同一的哈希码是可行的,但是却是不可取的。若返回相同的哈希码,那么在集合中使用这种对象将产生性能问题。在集合中检索对象时,会用到对象的哈希码做索引,这样检索效率就降低了
如下推荐一种比较好的做法。
这种做法能够保证生成哈希码的效率。又能使生成的哈希码至少位于一定范围之内,减少碰撞的次数。
特定类所具有的等同性判定方法
系统中一些类的等同性判定方法:
NSString,等同性判定方法:isEqualToString:
NSArray, 等同性判定方法:isEqualToArray:
NSDictionary:等同性判定方法:isEqualToDictionary:
这些方法如果如果受测对象类型不正确,将会抛出异常。
在编写自定类的判定方法时,也应一并覆写“isEqual:”方法。常见的实现方式为:如果受测的对象与接受消息的对象都属于同一个类,那么就调用自己编写的判定方法,否则就交由父类来判定。如下代码实现:
等同性判定的执行深度
创建等同性判定方法时,需要决定是根据整个对象来判断等同性,还是仅根据其中几个字段来判断。
深度等同性判定:以NSArray为例,NSArray检测方式为先看两个数组所含对象个数是否相等,若相等,则在每个对应位置的两个对象身上调用其“isEqual:”方法。如果对应位置上的对象均相等,那么这两个数组就相等。不过有时候,无须所有所有的数据都逐个比较。只根据部分数据即可判定,如对象中含有唯一标识符。
容器中可变类的等同性
在容器中放入可变类对象的时候,如果把某个对象放入集合中之后,就不应该在改变其哈希码了。集合会根据各个对象的哈希码来分装数据在不同的位置,如果某个对象在放入数组之后,哈希码又变了,那么其现在所在数组对这个对象来说就是错误的。更具体的介绍请参考书《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》中第八条的介绍。
要点总结:
1. 若想检测对象的等同性,请提供“isEqual:”与hash方法。
2. 相同的对象必具有相同的哈希码,但是两个哈希码相同的对象却未必相同。
3. 不要盲目的逐个检测每条属性,而是应该根据具体的需求来定制检测方案。
4. 编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。
注:根据《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》进行摘抄和整理。
根据“等同性”来比较对象是一个非常有用的功能。不过通过==操作符比较出来的结果未必是我们想要的,因为该操作比较的是两个指针本身,而不是其所指的对象。应该使用NSObject协议中声明的“isEqual:”方法来判定两个对象的等同性。一般来说,两个类型不同的对象总是不相等的。某些对象对象提供了特殊的“等同性判定方法”,如果已经知道两个受测对象都属于同一个类,那么就可以使用这种方法。以下述代码为例:
NSString *foo = @"Badger 123"; NSString *bar = [NSString stringWithFormat:@"Badger %i",123]; BOOL equalA = (foo == bar);// NO BOOL equalB = [foo isEqual:bar];//YES BOOL equalC = [foo isEqualToString:bar];//YES
可以看到==与等同性判定方法之间的差别。NSString类实现了一个自己所独有的等同性判定方法,名为“isEqualToString:”。传递给该方法的对象必须是NSString,否则结果为未定义。调用该方法比调用isEqual:方法快,后者还要执行额外的步骤,因为要判断受测对象的类型。
NSObject协议中有两个用于判定等同性的关键方法:
- (BOOL)isEqual:(id)object; - (NSUInteger)hash;
NSObject类对这两个方法的默认实现是:当且仅当“指针指”完全相等时,这两个对象才相等。若想在自定义的对象中正确覆写这些方法,就必须先理解其中的约定。如果“isEqual:”方法判定两个对象相等,那么其hash方法必须返回同一个值。但是,如果两个对象的hash方法返回同一个值,那么“isEqual:”方法未必会认为两者相等。
比如有下面这个类:
@interface EOCPerson : NSObject @property (nonatomic, copy) NSString *firstName; @property (nonatomic, copy) NSString *lastName; @property (nonatomic, assign) NSUInteger age; @end
一般认为,如果两个EOCPerson的所有字段均相等,那么这两个对象就相等。于是“isEqual:”方法就可以写成:
- (BOOL)isEqual:(id)object { if (self == object) return YES;// 1.判断两个对象的指针是否相同 if ([self class] != [object class]) return NO;//2.判断两个对象所属的类 EOCPerson *otherPerson = (EOCPerson *)object; if (![_lastName isEqualToString:otherPerson.lastName]) return NO; if (_age != otherPerson.age) return NO;//3.判断对象的属性值 return YES; }
首先,直接判断两个指针是否相等。若相等,则其均指向同一个对象,所以受测的对象也必定相等。
比较两个类所属的类。若不属于同一个类,则两个对象不相等。不过有时我们可能认为:一个父类实例可以与其子类实例相等。在继承体系中判断等同性时,经常遭遇到此类问题。所以实现“isEqual:”方法时要考虑到这种情况。
检测每个属性是否相等。只要其中有不相等的属性,就判定这两个对象不相等,否则两个对象相等。
实现hash方法。根据等同性约定:若两个对象相等,则其哈希码也相等,但是两个哈希码相等的对象却未必相等。这是能否正确覆写“isEqual:”方法的关键所在。
哈希方法的实现:
返回同一的哈希码是可行的,但是却是不可取的。若返回相同的哈希码,那么在集合中使用这种对象将产生性能问题。在集合中检索对象时,会用到对象的哈希码做索引,这样检索效率就降低了
如下推荐一种比较好的做法。
- (NSUInteger)hash { NSUInteger firstNameHash = [_firstName hash]; NSUInteger lastNameHash = [_lastName hash]; NSUInteger ageHash = _age; return firstNameHash ^ lastNameHash ^ ageHash; }
这种做法能够保证生成哈希码的效率。又能使生成的哈希码至少位于一定范围之内,减少碰撞的次数。
特定类所具有的等同性判定方法
系统中一些类的等同性判定方法:
NSString,等同性判定方法:isEqualToString:
NSArray, 等同性判定方法:isEqualToArray:
NSDictionary:等同性判定方法:isEqualToDictionary:
这些方法如果如果受测对象类型不正确,将会抛出异常。
在编写自定类的判定方法时,也应一并覆写“isEqual:”方法。常见的实现方式为:如果受测的对象与接受消息的对象都属于同一个类,那么就调用自己编写的判定方法,否则就交由父类来判定。如下代码实现:
- (BOOL)isEqualToPerson:(EOCPerson *)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:(EOCPerson *)object]; } else { return [super isEqual:object]; } }
等同性判定的执行深度
创建等同性判定方法时,需要决定是根据整个对象来判断等同性,还是仅根据其中几个字段来判断。
深度等同性判定:以NSArray为例,NSArray检测方式为先看两个数组所含对象个数是否相等,若相等,则在每个对应位置的两个对象身上调用其“isEqual:”方法。如果对应位置上的对象均相等,那么这两个数组就相等。不过有时候,无须所有所有的数据都逐个比较。只根据部分数据即可判定,如对象中含有唯一标识符。
容器中可变类的等同性
在容器中放入可变类对象的时候,如果把某个对象放入集合中之后,就不应该在改变其哈希码了。集合会根据各个对象的哈希码来分装数据在不同的位置,如果某个对象在放入数组之后,哈希码又变了,那么其现在所在数组对这个对象来说就是错误的。更具体的介绍请参考书《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》中第八条的介绍。
要点总结:
1. 若想检测对象的等同性,请提供“isEqual:”与hash方法。
2. 相同的对象必具有相同的哈希码,但是两个哈希码相同的对象却未必相同。
3. 不要盲目的逐个检测每条属性,而是应该根据具体的需求来定制检测方案。
4. 编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。
注:根据《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》进行摘抄和整理。
相关文章推荐
- org.hibernate.PersistentObjectException: detached entity passed to persist
- 多态、Object
- ubuntu下配置Objective-C(2.0)学习…
- user_segments和user_objects
- user_segments和user_objects
- CVPR2014 Objectness Estimation at 300fps
- objective-C学习 KVC
- Object-C--->Swift之(七)丰富的函数形参功能
- Objective-C Associated Objects 的实现原理
- 类 Object
- Object C 语法入门
- Scala的object和class的区别
- 【Objective-C】09-空指针和野指针
- Object-C--->Swift之(五)函数入门级讲解
- Objective-C UI之UITableView详解
- objective-c宏定义
- Objective-C 10 NSValue和NSNull使用方法
- 水晶报表设置FiledObject支持HTML格式的数据
- Android ObjectAnimator ValueAnimation学习
- IOS开发基础Object-C( 15)-- block的使用方法