Effective Objective-C 2.0 第8条:理解“对象等同性”概念
2015-10-25 23:02
423 查看
利用“等同性”来比较对象是一个比较常用的概念
==操作符比较判断两个指针本身,而不是其所指的对象NSObject协议中声明的“isEqual”方法来判断两个对象的等同性
NSString类实现了一个自己独有的等同判断方法,名叫“isEqualToString”,它比“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; //比较两对象所属的类,不属于同一个类,则两对象不相等 if ([self class] != [object class]) return NO; //检测每个属性是否相等,只要其中有不相等的属性,就判定两对象不等 EOCPerson *otherPerson = (EOCPerson*)object; if (![_firstName isEqualToString:otherPerson.firstName]) return NO; if (![_lastName isEqualToString:otherPerson.lastName]) return NO; if (_age != otherPerson.age) return NO; return YES; }
接下来就是实现hash方法了,根据等同性约定:若两对象相等,则其哈希码相同,这是能否正确复写“isEqual:”方法的关键。
第一种写法:
- (NSUInteger)hash { return 1337; }
对于collection中使用这种对象将产生性能问题,如果令每个对象都返回相同的哈希码,那么在set中已有1000000个对象的情况下,若是继续向其中添加对象,则需将这1000000个对象全部扫描一遍。
第二种写法:
- (NSUInteger)hash { NSString* stringToHash = [NSString* stringWithFormat:@"%@:%@:%i", _firstName, _lastName, _age]; return [stringToHash hash]; }
还需要负担创建字符串的开销,所以比返回单一值慢,把这种对象添加到collection中时,也会产生性能问题,因为想要添加,必须先计算其哈希值。
第三种写法:
- (NSUInteger) hash { NSUInteger firstNameHash = [_firstName hash]; NSUInteger lastNameHash = [_lastName hash]; NSUInteger ageHash = _age; return firstNameHash ^ lastNameHash ^ ageHash; }
这种方法既能保持高效率,又能使生成的哈希码至少位于一定的范围之内,而不会过于频繁地重复,此算法生成的哈希码还是会产生碰撞,不过至少保证哈希码有多重可能的取值。
特定类所具有的等同性判定方法
除了NSString之外,NSArray和NSDictionary类也具有特殊的等同性判定方法,前者名为“isEqualToArray:”,后者名为“isEqualToDictionary:”。如果和其相比较的对象不是数组或者字典,那么这两个方法会各自抛出异常。如果经常需要判断等同性,那么可能会自己来创建等同性判断方法,因为无须检测参数类型,所以能大大提升检测速度。
在编写判定方法时,也应一并覆写“isEqual:”方法。isEqual方法常见的实现方式为:如果受测的参数与接收该消息的对象都属于同一个类,那么就调用自己的编写的判定方法,否则就交给超类来判断。
- (BOOL)isEqualToPerson:(EOCPerson*)otherPerson { } - (BOOL)isEqual:(id)object { if ([self class] == [object class]) { return [self isEqualToPerson:(EOCPerson*)object]; } else { return [super isEqual:object]; } }
等同性判定的执行深度
对于NSArray判断等同性,首先数组个数相同,然后对每个位置上对象调用“isEqual:”方法。这叫“深度等同性判定”。不过有时无须将所有数据逐个比较,只根据其中部分数据即可判明二者是否相同,比如,假设EOCPerson类的实例是根据数据库里的数据创建出来,那么可能会含有另外一个属性,此属性是“唯一标示符”。在此情况下,就只用根据标示符来判断等同性了。
容器中可变类的等同性
还有一种情况一定要注意,就是在容器中放入可变类对象的时候。把某个对象放入collection之后,就不应该再改变其哈希值了。如果对于某个对象在放入箱子后哈希吗变了,那么其现在所处的这个箱子对他来说就是“错误”的。想要解决这个问题,需要确保哈希码不是根据对象的“可变部分”计算出来的,或是保证放入collection之后就不在更改对象了。例如:NSMutableSet(Set是不允许一个对象出现两次的)
1
NSMutableSet *set = [NSMutableSet new]; NSMutableArray *arrayA = [@[@1, @2] mutableCopy]; [set addObject:arrayA]; //set = {((1, 2))}
2
NSMutableArray *arrayB = [@[@1, @2] mutableCopy]; [set addObject:arrayB]; //set={((1, 2))}
3
NSMutableArray *arrayC = [@[@1] mutableCopy]; [set addObject:arrayC]; //set={((1), (1, 2))}
4
[arrayC addObject:@2]; //set={((1, 2), (1, 2))}
5
NSSet *setB = [set copy]; //setB = {((1, 2))}
复制过的set中只剩下一个对象了。
把某对象放入collection之后改变其内容将会造成何种后果,如果非要这么做,需要利用相应地代码处理可能发生的问题。
相关文章推荐
- oc 的一些概念
- [代码例程] iPhone开发入门(7)--- 从C/C++语言到Objective-C语
- IOS 面试习题 Object-C
- objective-c block 讲解
- IOS求职之OC面试题
- iOS OC和Swift混编
- OC_继承,初始化
- OC 省市区划分
- iOS开发 — Quartz 2D知识点应用 (制作了一个Demo,源代码)
- 编写 Objective-C 代码/掌握基本的编程技能 之 读后笔记
- Objective-C 类,对象,属性,方法
- Objective - C 字符串 数组 字典 集合的基本使用
- 图形上下文解释CGContextSaveGState/CGContextRestoreGState
- Objective-C更改placeholder字体颜色
- Objective-C关键字self和super详解
- 字符串的获取
- 1.c++中与oc中类和对象创建及访问
- 2.c++与objective-c中的构造方法(构造函数)
- UIWebView中Html中用JS调用OC方法及OC执行JS代码
- c语言入门(一)