iOS 使用isEqual、 == 、isEqualToArray 、 isEqualToString

两个概念 相等性与本体性

来看段代码,猜猜结果如何

NSArray *a = @[@1];
NSArray *b = a;
NSArray *c = @[@1];

if (a == b) {
    NSLog(@"a == b");
}

if (a == c) {
    NSLog(@"a == c");
}

if ([a isEqualToArray:c]){
    NSLog(@"a c 具有本体性");
}

打印结果

a == b
a c 具有本体性
这段代码,首先我们要搞明白内存到底是怎么分配的。
a,b 都是指针。
[NSObject new]则在堆上分配了一块内存空间
该内存空间的首地址被存储在 a 中。
将a的值 赋值给 b.

如图:

相等性:当两个物体有一系列相同的可观测的属性时,两个物体可能是互相相等或者等价的。但这两个物体仍然是不同的,他们各自有自己的本体。
本体性:在编程中,一个对象的本体和它的内存地址是相互关联的。关联的内存地址相同则具有本体性。

三种判等方式
你可能会对以下三种判等方式的实现有疑惑

isEqual:

isEqualToArray:
Objective-C 对于判等的实现

接触了 相等性 和 本体性之后, 来看看 Objective-C 对于判等是如何实现的.

isEqual:

NSObject 使用 isEqual: 这个方法来测试和其他对象的相等性。在它的基类实现中,相等性检查本质上就是对本体性的检查。两个 NSObject 如果指向了同一个内存地址,那它们就被认为是相同的。

子类如果需要重写isEqual:

实现一个新的 isEqualTo__ClassName__ 方法,进行实际意义上的值的比较。
重写 isEqual: 方法进行类和对象的本体性检查,如果失败则回退到上面提到的值比较方法。
重写 hash 方法
==

是对本体性的比较,比较指针。

isEqualToArray:

是对相等性进行检查。
NSArray 作为NSObject的子类,实现了 isEqualToArray:的方法。
isEqualToArray:对 相等性进行检查。

相等性检查的实现

在编程中,是如何实现相等性检查的呢?
借助Hash来实现。
关于Hash的知识,Wiki_Hash

如果两个对象相等,它们的 hash 值也一定是相等的(反之不然)
对于自定义的子类来说,判断相等性需要实现hash方法

Object comparison
给NSHipster纠个错:
Swift-Comparison-Protocols
中提到的:
Objective-C 中对于对象的比较,== 操作符的运算结果就是来自 isEqual: 方法的结果:
根据文档的意思是有问题的
Each class determines equality for its instances by implementing class-specific comparison logic. The root class, NSObject, measures equality by simple pointer comparison; at the other extreme, the determinants of equality for a custom class might be class membership plus all encapsulated values.
Example:
来自NSHipster

@interface Person
@property NSString *name;
@property NSDate *birthday;

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

@implementation Person

  • (BOOL)isEqualToPerson:(Person *)person {
    if (!person) {
    return NO;
    }

    BOOL haveEqualNames = (!self.name && !person.name) || [self.name isEqualToString:person.name];
    BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday];

    return haveEqualNames && haveEqualBirthdays;
    }

pragma mark - NSObject

  • (BOOL)isEqual:(id)object {
    if (self == object) {
    return YES;
    }

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

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

  • (NSUInteger)hash {
    return [self.name hash] ^ [self.birthday hash];
    }
    Swift 中的判等

(Swift 并不是我的主力语言 简单介绍下)

Swift === 判读本体性
Swift == 判断相等性

Objective-C == 判断两个对象是否是同一个引用
你可以通过重写isEqual:方法来改变==的实现。
如果没有重写,默认调用的NSObject的实现。
或者更进一步去实现类似 -isEqualToString: 这样的 -isEqualToClass: 的带有类型信息的方法来进行内容判等。

在 Swift 中情况大不一样,Swift 里的 == 是一个操作符的声明,在 Equatable 里声明了这个操作符的接口方法:

protocol Equatable {
func ==(lhs: Self, rhs: Self) -> Bool
}
实现这个接口的类型需要定义适合自己类型的==操作符,如果我们认为两个输入有相等关系的话,就应该返回true.

Swift 中于 OC == 相对应地是 === 操作符

一些特殊情况

NSString 与 字符串驻留

NSString *s1 = @"123";
NSString *s2 = @"123";
NSString *s3 = [NSString stringWithFormat:@"%@",@"123"];

if (s1 == s2) {
    NSLog(@"s1 == s2");
}

if (s1 == s3) {
    NSLog(@"s1 == s3");
}

if ([s1 isEqualToString:s3]) {
    NSLog(@"s1 isEqualToString:s3");
}

打印:

s1 == s2
s1 isEqualToString:s3
由于使用了字符串驻留技术
在使用字符串字面量来初始化NSString时,字符串驻留技术把一个不可变字符串对象的值拷贝给各个不同的指针。s1 和 s2都指向同样一个驻留字符串值。

这就导致了 s1 s2 指向相同的内存地址。
s1 s3 指向的内存地址不同,但是值是相同的。

类族对于判等的影响

首先什么是类族?简单介绍下:
Cocoa框架中的类很多都是类族,隐藏了背后的实现。NSNumber,NSArray,还有众多collection类都是类族。

  • (UIButton *)buttonWithType:(UIButtonType)type;
    该方法返回的对象,其类型取决于传入的按钮类型。(这就隐藏了实现细节,用户只需根据需求传入不同的UIButtonType)

如果我们用对象的类型进行判等自然会出现问题:

id maybeAnArray = /* ...*/;
if ([maybeAnArray class] == [NSArray class]) {
//will never be hit
}
如果不知道类族,你会迷惑这段代码的问题到底出在何处。
我们可以改用 isKindOfClass: 方法来进行判断。
isKindOfClass来确定一个对象是否是一个类的成员,或者是派生自该类的成员
由于类簇中的类 是 抽象类的子类,所以 isKindOfClass: 来判等是可行的。

转自掘金

你可能感兴趣的:(iOS 使用isEqual、 == 、isEqualToArray 、 isEqualToString)