iOS-对象相等性

作者: FlyElephant | 来源:发表于2018-03-30 19:19 被阅读12次

Objective-C中对象的相等性是经常被忽略的一块,开发中经常用到isEqual,isEqualToString,==进行对象比较.Objective-C如果两个对象的内存地址一样,那么对象肯定相等,可以称之为对象本体性.
如果两个对象内存地址不一样,但是其他的数据都一样,那么对象也相等,可以称之为等同性.

<pre><code>- (BOOL)isEqual:(id)obj { return obj == self; }</code></pre>

在 Foundation 框架中,一般NSObject 的子类都有自己的相等性检查实现,例如:

  • NSAttributedString -isEqualToAttributedString:
  • NSData -isEqualToData:
  • NSDate -isEqualToDate:
  • NSDictionary -isEqualToDictionary:
  • NSHashTable -isEqualToHashTable:
  • NSIndexSet -isEqualToIndexSet:
  • NSNumber -isEqualToNumber:
  • NSOrderedSet -isEqualToOrderedSet:
  • NSSet -isEqualToSet:
  • NSString -isEqualToString:
  • NSTimeZone -isEqualToTimeZone:
  • NSValue -isEqualToValue:

如果想比较对象是否相等,建议使用以上方法进行比较,而不是简单的通过isEqual进行对象比较.

字符串

不要通过==进行字符串判断,代码如下:

<pre><code>` NSString *str1 = @"FlyElephant";
NSString *str2 = @"FlyElephant";

NSLog(@"%p---%p",str1,str2);
if (str1 == str2) {
    NSLog(@"字符串相等");
}`</code></pre>

👆的代码我们期望是不相等,但是因为字符串驻留的优化技术,所有的字符串都是在一个共享字符串池中的,将不可变字符串的值赋值给了不同的指针.

isEqual

比较两个对象是否相同,需要根据具体的对象的数据进行系统比较,建立Person类,定义姓名,年龄,地区三个字段:

<pre><code>`@interface Person : NSObject<NSCopying>

@property (copy, nonatomic) NSString *name;

@property (assign, nonatomic) NSUInteger age;

@property (copy, nonatomic) NSString *location;

  • (BOOL)isEqualToPerson:(Person *)person;

@end`</code></pre>

<pre><code>`- (BOOL)isEqualToPerson:(Person *)person {

if (!person) {
    return NO;
}

BOOL isEqualName = (!self.name && !person.name) || [self.name isEqualToString:person.name];
BOOL isEqualAge = self.age == person.age;
BOOL isEqualLocation = (!self.location && !person.location) || [self.location isEqualToString:person.location];

return isEqualName & isEqualAge & isEqualLocation;

}`</code></pre>

如果比较两个Person是否相等,通过isEqualPeron来比较:
<pre><code>` Person *person1 = [[Person alloc] init];
person1.name = @"FlyElephant";
person1.age = 27;
person1.location = @"北京";

Person *person2 = [[Person alloc] init];
person2.name = @"FlyElephant";
person2.age = 27;
person2.location = @"北京";

BOOL isEqulPerson = [person1 isEqualToPerson:person2];

if (isEqulPerson) {
    NSLog(@"person1与person2相等");
} else {
    NSLog(@"两者不相等");
}`</code></pre>

如果传入对象就是自身,那么就不需要进行之后的判断,同时为了避免出现较多的比较方法,重写isEqual方法,将isEqualToPerson方法内置在其中即可.

<pre><code>`- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}

if (![object isKindOfClass:[Person class]]) {
    return NO;
}

return [self isEqualToPerson:(Person *)object];

}`</code></pre>

通过isEqual的判断结果与isEqualPerson结果一致.
<pre><code>BOOL isEqual = [person1 isEqualToPerson:person2]; if (isEqual) { NSLog(@"isEqual:person1与person2相等"); } else { NSLog(@"isEqual:两者不相等"); }</code></pre>

hash

对象比较的过程经常会听到如果两个对象相等,那么hash值一定相等,两个对象的hash值相等,对象不一定相等.

👆代码中person1与person2的比较过程并没有调用hash,那么什么时候会调用对象的hash方法呢?

<pre><code>- (NSUInteger)hash { NSUInteger hash = [super hash]; NSLog(@"Person调用hash方法 = %ld", hash); return hash; }</code></pre>

通过Objective-C集合类,NSMutableArray,NSMutableSet,NSMutableDictonary添加Person对象.

<pre><code>` Person *person1 = [[Person alloc] init];
person1.name = @"FlyElephant";
person1.age = 27;
person1.location = @"北京";

Person *person2 = [[Person alloc] init];
person2.name = @"FlyElephant1";
person2.age = 28;
person2.location = @"北京";

NSMutableArray *arr1 = [[NSMutableArray alloc] initWithObjects:person1, nil];
NSMutableArray *arr2 = [[NSMutableArray alloc] initWithObjects:person2, nil];

NSLog(@"数组比较结束----------------------");
NSMutableSet *set1 = [NSMutableSet set];
[set1 addObject:person1];
NSMutableSet *set2 = [NSMutableSet set];
[set2 addObject:person2];
NSLog(@"NSMutableSet添加结束 -------------------------------");

NSMutableDictionary *dictionaryValue1 = [NSMutableDictionary dictionary];
[dictionaryValue1 setObject:person1 forKey:@"dic1"];
NSMutableDictionary *dictionaryValue2 = [NSMutableDictionary dictionary];
[dictionaryValue2 setObject:person2 forKey:@"dic2"];
NSLog(@"dictionary 将person设置为值-------------------------------");

NSMutableDictionary *dictionaryKey1 = [NSMutableDictionary dictionary];
[dictionaryKey1 setObject:@"1" forKey:person1];
NSMutableDictionary *dictionaryKey2 = [NSMutableDictionary dictionary];
[dictionaryKey2 setObject:@"2" forKey:person2];
NSLog(@"dictionary 将person设置为键 -------------------------------");`</code></pre>

<pre><code>数组比较结束---------------------- Person调用hash方法 = 106102872298336 Person调用hash方法 = 106102872298336 Person调用hash方法 = 106102872298432 Person调用hash方法 = 106102872298432 NSMutableSet添加结束 ------------------------------- dictionary 将person设置为值------------------------------- Person调用hash方法 = 106102872298336 Person调用hash方法 = 106102872298496 Person调用hash方法 = 106102872298432 Person调用hash方法 = 106102872298528 dictionary 将person设置为键 -------------------------------</code></pre>

Person设置为字典的key值时需要实现NSCopying协议:
<pre><code>- (id)copyWithZone:(NSZone *)zone { Person *model = [[[self class] allocWithZone:zone] init]; model.name = self.name; model.age = self.age; model.location = self.location; return model; }</code></pre>

散列表(Hash Table)是程序设计中基础的数据结构之一,它使得 NSSet 和NSDictionary能够非常快速地(O(1)) 进行元素查找.

数组中的元素在内存中的分布是连续的地址,如果需要判断某个元素是否存在,需要进行逐步遍历.

散列表通过散列函数计算出散列值,实现元素的均匀分布,当不同的元素出现相同的散列值会出现散列碰撞.散列表同样可以通过元素的散列值很快查找到元素.

NSSet会根据元素的散列值判断元素重复,NSDictionary中key是不能重复的,所有会要求元素的散列值唯一,这两个集合会调用对象的hash方法.

不同元素的hash方法实现可以有不同的方式,如果是一个时间对象,hash实现如下:

<pre><code>- (NSUInteger)hash { return (NSUInteger)abs([self timeIntervalSinceReferenceDate]); }</code></pre>

对于UIColor对象,可以通过位移实现:

<pre><code>- (NSUInteger)hash { CGFloat red, green, blue; [self getRed:&red green:&green blue:&blue alpha:nil]; return ((NSUInteger)(red * 255) << 16) + ((NSUInteger)(green * 255) << 8) + (NSUInteger)(blue * 255); }</code></pre>

大神Mattt Thompson建议:
大多数情况我们只需要对于关键属性的散列值进行一个简单的 异或(XOR)操作,就能够满足在 99% 的情况下的需求了.

<pre><code>- (NSUInteger)hash { return [self.name hash] ^ [self.location hash]; }</code></pre>

参考链接:
iOS-isEqual,isEqualToString和==区别
equality
iOS开发 之 不要告诉我你真的懂isEqual与hash!

相关文章

  • iOS-对象相等性

    Objective-C中对象的相等性是经常被忽略的一块,开发中经常用到isEqual,isEqualToStrin...

  • C# 相等比较

    引用相等性 判断两个对象是否引用同一基础对象。使用 ReferenceEquals 方法判断: 值相等性 使用 =...

  • JavaScript对象相等性判断

    函数isEqual接收两个对象, 判断它们的相等性, 返回boolean 根据相等判断的实用性, 做出如下的相等性...

  • 对象相等性isEqual

    当你要实现相等性的时候记住这个约定:你需要同时实现isEqual 和 hash方法。如果两个对象是被isEqual...

  • Chapter 30《Object Equality》

    Scala中的相等性 Scala的相等性比较和Java中的不同,在Java中使用==表示两个对象的引用相等性,使用...

  • 数据类和类委托

    1.1、通用对象方法 toString、equals、hashCode 。 equals()对象相等性:在Kotl...

  • IdentityHashMap简介

    IdentityHashMap利用哈希表实现Map接口,不同的是,其比较键(或值)时,使用引用相等性代替对象相等性...

  • Java1.8-IdentityHashMap源码解析

    概述   IdentityHashMap利用Hash表来实现Map接口,比较键(和值)时使用引用相等性代替对象相等...

  • kotlin基础笔记之相等性

    Kotlin 中有两种类型的相等性: 引用相等(两个引用指向同一对象)结构相等(用 equals() 检查) 引用...

  • C#/.NET中的相等性与同一性(==、Equals和Refer

    开发过程中,经常需要编码来对不同的对象进行比较。在对两个对象是否相等的判定中从在这样两个概念,相等性和同一性。简单...

网友评论

本文标题:iOS-对象相等性

本文链接:https://www.haomeiwen.com/subject/lhxwhxtx.html