判斷物件是否相等的方法,==與 isEqual

史前圖騰發表於2017-12-13

  • == 比較兩個物件的記憶體地址

  • isEqualToString: 比較兩個字串的內容

  • isEqual: 預設情況下是比較兩個物件的記憶體地址,但是有一些系統自帶的類(比如Foundation中的NSString,NSArray等)重寫了這個方法,改變了這個方法的判斷規則(一般改為比較兩個物件的內容,不是記憶體地址)

陣列NSArray中的containsObject:,indexOfObject:方法就是利用isEqual來比較的,所以陣列中的字串內容一樣,也會被認為是一樣的

- (void)test1
{
   NSString *string1 = @"jack";
   NSString *string2 = [NSString stringWithFormat:@"jack"];

//理論上儲存在記憶體不同區域,為不同物件,但系統重寫後改成了比較內容
   NSLog(@"%p %p", string1, string2);

   NSLog(@"string1 == string2 -> %zd", string1 == string2); // 結果為0

   NSLog(@"[string1 isEqualToString:string2] -> %zd", [string1 isEqualToString:string2]); // 結果為1

   NSLog(@"[string1 isEqual:string2] -> %zd", [string1 isEqual:string2]); // 結果為1

}
複製程式碼

同理下面的兩個陣列並不是同一個,但是內容相同,因此會被判定為相同

- (void)test2
{
    NSString *string1 = [NSString stringWithFormat:@"111"];
    NSString *string2 = [NSString stringWithFormat:@"222"];
    //兩個儲存不同物件的陣列,但內容是相同的
    NSArray *array1 = @[string1, @"222", @"333"];
    NSArray *array2 = @[@"111", string2, @"333"];
    
    NSArray *array = @[array1, array2];
    
    NSLog(@"%zd", [array indexOfObject:array2]);//結果為0
}
複製程式碼

如果有兩個內容相同的MYPerson物件,則會被認為是不同的,因為此時是對記憶體地址進行比較,看是不是同一個物件

//沒有重寫isEqual
- (void)test3
{
    MYPerson *p1 = [[MYPerson alloc] init];
    p1.age = 20;
    p1.no = 30;
    
    MYPerson *p2 = [[MYPerson alloc] init];
    p2.age = 20;
    p2.no = 30;
    
    NSLog(@"%p %p", p1, p2);
    NSLog(@"p1 == p2 -> %zd", p1 == p2); // 0
    NSLog(@"[p1 isEqual:p2] -> %zd", [p1 isEqual:p2]); // 0
}
複製程式碼

如果希望MYPerson也能像系統的類NSString一樣,能夠對內容相同的兩個不同物件也認定為相同,需要重寫isEqual方法

//重寫後
- (void)test3
{
    MYPerson *p1 = [[MYPerson alloc] init];
    p1.age = 20;
    p1.no = 30;
    
    MYPerson *p2 = [[MYPerson alloc] init];
    p2.age = 20;
    p2.no = 30;

    NSArray *array = @[@"2", @"6", p1, @"111"];
    
    NSLog(@"%zd", [array indexOfObject:p2]);//結果為2
}
複製程式碼

MYPerson中重寫程式碼如下:


#import <Foundation/Foundation.h>

@class MYCar;

@interface MYPerson : NSObject
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) NSInteger no;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) MYCar *car;

- (BOOL)isEqualToPerson:(MYPerson *)person;
@end

複製程式碼
#import "MYPerson.h"
#import "MYCar.h"

/*
 一旦重寫了isEqual:方法,最好重寫hash方法,而且要遵守以下原則:
 1.isEqual:返回YES的2個物件,hash值一定要一樣
 2.hash值一樣的2個物件,isEqual:返回不一定是YES
 */

@implementation MYPerson

- (NSUInteger)hash
{
    return self.age + self.no + self.name.hash + self.car.hash;
}

- (BOOL)isEqual:(MYPerson *)object
{
    return [self isEqualToPerson:object];
}

- (BOOL)isEqualToPerson:(MYPerson *)person
{
    // 如果是完全相同的物件,就省去後面的判斷
    if (self == person) return YES;
    
    // 如果object的型別不對,就不需要比較
    if (![person isKindOfClass:self.class]) return NO;
    
    // 基本資料型別
    BOOL result = (self.age == person.age && self.no == person.no);
    if (result == NO) return result;
    
    // 物件型別,兩個物件為nil時isEqual:的結果為0(NO),所以需要專門處理
    if (self.name || person.name) {
        if (![self.name isEqual:person.name]) return NO;
    }
    
    if (self.car || person.car) {
        if (![self.car isEqual:person.car]) return NO;
    }
    
    return YES;
}

@end

複製程式碼

相關文章