最近在新接手的项目中进行对象比较,对同一个对象调用isEqual来比较,结果居然是NO。猜测是对象重写了isEqual
方法。查看代码以下:算法
isEqual
方法,虽然方法不太严谨,没有首先判断==,代码看起来也什么大问题,可是同一个对象比较也不该该返回NO啊。看了下面一堆&&判断,难道要一个个po吗?忽然想到了
二分查找
算法(算法基础仍是有用的)的优势,而后快速定位到放回NO的条件。
(lldb) po (BOOL)([self.releasetime isEqualToString:info.releasetime])
NO
(lldb)
复制代码
解决办法:编程
场景一:针对当前的场景,只须要在的最顶部添加下面判断就能够了
if (self == object) return YES;
数组
场景二:可是一些其余场景,咱们确实会遇到比较两个地址不同,可是数据同样(而且对象有的属性确实为
nil
)的场景。这个时候咱们能够为NSString
等基本数据类型添加category
,而后重写isEqualToString:
方法,若是要比较的两个对象都是nil
则返回YES
。可是若是要比较的两个对象都不是nil并且length
也相同,难道我还要重写一个方法进行遍历比较吗?能不能直接用原来的isEqualToString:
方法,可是category
中不能调用super
方法,再说分类也不是子类。此时咱们就须要利用runtime
遍历方法列表找到原来的方法,而后调用一下返回结果就能够了。后来发现,想多了,给nil发送消息返回的始终是NO。最后在分类中实现一个类方法判断一下就能够了。bash
#import "NSString+isEqual.h" @implementation NSString (isEqual) +(void)load { NSLog(@"结果1 = %@", [nil isEqualToString:nil] ? @"YES" : @"NO"); NSLog(@"结果2 = %@", [NSString isString:nil EqualToString:nil] ? @"YES" : @"NO"); } +(BOOL)isString:(NSString *)aString EqualToString:(NSString *)bString { if (aString == nil && bString == nil ) { return YES; } return [aString isEqualToString:bString]; } @end 复制代码
基本类型
, ==运算符比较的是值;对象类型
, ==运算符比较的是对象的地址isEqual
方法就是用来判断两个对象是否相等(自定义对象须要重写isEqual)isEqual方法是NSObject中声明的,默认实现就是简单的比较对象地址。markdown
@implementation NSObject (Approximate) - (BOOL)isEqual:(id)object { return self == object; } @end 复制代码
-(BOOL)isEqual:(id)object
-(NSUInteger)hash
-(id)copyWithZone:(NSZone *)zone
注意: 使用OC自定义的类实例做为NSDictionary的key的话,须要实现NSCoping协议,若是不实现的话,向dictionary中添加数据时会报警告:Sending 'Coder *__strong to parameter of incompatible type 'id _Nonnull',在运行的时候,在setObject forKey函数这里直接崩溃数据结构
-isEqualToAttributedString:
-isEqualToData:
-isEqualToDate:
-isEqualToDictionary:
-isEqualToHashTable:
-isEqualToIndexSet:
-isEqualToNumber:
-isEqualToOrderedSet:
-isEqualToSet:
-isEqualToString:
-isEqualToTimeZone:
-isEqualToValue:
containsObject:
和removeObject:
方法都是使用了isEqual来判断成员是否相等的isEqual
继续判断是否为两个相同对象目的:为了提升查找的速度函数
和数组相比, 基于hash值索引的Hash Table查找某个成员的过程就是oop
HashTable是一种基本数据结构,NSSet和NSDictionary都是使用HashTable存储数据的,所以能够能够确保他们查询成员的速度为O(1)。而NSArray使用了顺序表存储数据,查询数据的时间复杂度为O(n)。优化
Coder* coder1 = [Coder initWith:@"C++" level:@"11"]; Coder* coder2 = [Coder initWith:@"C++" level:@"11"]; Coder* coder3 = [Coder initWith:@"C++" level:@"17"]; NSSet* coderSet = [NSSet setWithObjects:coder1, coder2, coder3, nil]; NSLog(@"coderSet.count = %ld", coderSet.count); 复制代码
Coder* coder1 = [Coder initWith:@"C++" level:@"11"]; Coder* coder2 = [Coder initWith:@"C++" level:@"11"]; Coder* coder3 = [Coder initWith:@"C++" level:@"17"]; NSMutableDictionary* coderDic2 = [NSMutableDictionary dictionary]; [coderDic2 setObject:@"1" forKey:coder1]; [coderDic2 setObject:@"2" forKey:coder2]; [coderDic2 setObject:@"3" forKey:coder3]; NSLog(@"coderDic2.count = %ld", coderDic2.count); 复制代码
isEqual
进行判断是否真的相等.h文件atom
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface Coder : NSObject<NSCopying> @property (nonatomic, strong) NSString *language; @property (nonatomic, strong) NSString *level; +(instancetype)initWith:(NSString*)language level:(NSString*)level; @end NS_ASSUME_NONNULL_END 复制代码
.m文件
#import "Coder.h" @implementation Coder +(instancetype)initWith:(NSString*)language level:(NSString*)level { Coder* coder = [[Coder alloc] initWith:language level:level]; return coder; } -(instancetype)initWith:(NSString*)language level:(NSString*)level { self.language = language; self.level = level; return self; } // 对象用做NSSDictionary的key必须实现 -(id)copyWithZone:(NSZone *)zone { Coder* coder = [[Coder allocWithZone:zone] initWith:self.language level:self.level]; return coder; } -(BOOL)isEqual:(id)object { NSLog(@"func = %s", __func__); if (self == object) { return YES; } if (![object isKindOfClass:[self class]]) { // object == nil 在此返回NO return NO; } return [self isEqualToCoder:object]; } -(BOOL)isEqualToCoder:(Coder*)object { // isEqualToString 须要使用分类重写一下,不然 [nil isEqualToString:nil]会返回NO if (![self.language isEqualToString:object.language]) { return NO; } if (![self.level isEqualToString:object.level]) { return NO; } return YES; } -(NSUInteger)hash { BOOL isCareAddress = YES; NSUInteger hashValue = 0; if (isCareAddress) { // 若是指望对地址不一样、可是内容相同的对象作区分 hashValue = [super hash]; // 结果:两个地址不一样,可是内容相同的对象添加到NSMutableSet中,NSMutableSet的个数返回的是2 } else { // 不关心地址是否相同,只对内容进行区分(对关键属性的hash值进行按位异或运算做为hash值) hashValue = [self.language hash] ^ [self.level hash]; // 结果:两个地址不一样,可是内容相同的对象添加到NSMutableSet中,NSMutableSet的个数返回的是1 } NSLog(@"func = %s, hashValue = %ld", __func__, hashValue); return hashValue; } @end 复制代码
调用
#import "HashViewController.h" #import "Coder.h" @interface HashViewController () @end @implementation HashViewController - (void)viewDidLoad { [super viewDidLoad]; Coder* coder1 = [Coder initWith:@"C++" level:@"11"]; Coder* coder2 = [Coder initWith:@"C++" level:@"11"]; Coder* coder3 = [Coder initWith:@"C++" level:@"17"]; NSArray* coderList = @[coder1, coder2, coder3]; NSLog(@"array-containsObject-coder1-start"); [coderList containsObject:coder1]; NSLog(@"array-containsObject-coder1-end"); NSLog(@"array-containsObject-coder2-start"); [coderList containsObject:coder2]; NSLog(@"array-containsObject-coder2-end"); NSLog(@"array-containsObject-coder3-start"); [coderList containsObject:coder3]; NSLog(@"array-containsObject-coder3-end"); NSLog(@"coderList.count = %ld", coderList.count); NSSet* coderSet = [NSSet setWithObjects:coder1, coder2, coder3, nil]; NSLog(@"coderSet.count = %ld", coderSet.count); NSDictionary* coderDic = @{@"1":coder1, @"2":coder2, @"3":coder3}; NSLog(@"coderDic.count = %ld", coderDic.count); NSMutableDictionary* coderDic2 = [NSMutableDictionary dictionary]; [coderDic2 setObject:@"1" forKey:coder1]; [coderDic2 setObject:@"2" forKey:coder2]; [coderDic2 setObject:@"3" forKey:coder3]; NSLog(@"coderDic2.count = %ld", coderDic2.count); } @end 复制代码