Как сравнить равенство структур / свойств из двух объектов NSO - PullRequest
0 голосов
/ 01 февраля 2012

У меня есть класс базы данных NSObject с примерно 100 свойствами - в основном NSStrings, но некоторые NSDates. Я пытаюсь определить простой способ сравнить любые два заданных объекта-члена базы данных. Например, если пользователь редактирует свойство в objectA, я хочу иметь возможность сравнить его с исходным объектом в БД, чтобы увидеть, были ли внесены изменения.

[objectA isEqual:objectB] не возвращает действительные результаты

поэтому я переопределил метод isEqual в определении класса, но это утомительно. Я также должен проверить каждое свойство, чтобы увидеть, является ли одно из них нулем с датами, поскольку сравнения не работают, если один из них равен нулю.

Ответы [ 4 ]

1 голос
/ 01 февраля 2012

Может быть, вы можете использовать KVC, чтобы сделать переопределение isEqual более кратким.

Сначала необходимо подготовить массив NSString, который представляет каждое имя свойства, возможно, в методе init.

- (id) init {

    // Other implementation of init

    self.propertyNames = [NSArray arrayWithObjects:@"property1",
                                                   @"property2",
                                                   @"property3",
                                                   ...
                                                   @"propertyN", nil];
}

В методе isEqual вы просматриваете все имена свойстви получить значение свойства, используя метод valueForKey.

- (BOOL) isEqual : (id) object {
    for(NSString * propertyName in self.propertyNames) {
        id obj1 = [self valueForKey:property];
        id obj2 = [object valueForKey:property];

        if([obj1 isKindOfClass:NSStringFromClassName(@"NSString")]) {
            if(![obj2 isKindOfClass:NSStringFromClassName(@"NSString")])
                return NO;

            NSString * strObj1 = (NSString *)obj1;
            NSString * strObj2 = (NSString *)obj2;
            if(![strObj1 isEqualToString:strObj2])
                return NO;
        }

        if([obj1 isKindOfClass:NSStringFromClassName(@"NSDate")]) {
            if(![obj2 isKindOfClass:NSStringFromClassName(@"NSDate")])
                return NO;

            NSDate * dateObj1 = (NSDate *)obj1;
            NSDate * dateObj2 = (NSDate *)obj2;
            if(![dateObj1 isEqualToDate:dateObj2])
                return NO;
        }
    }
    return YES;
}

Таким образом, вам не нужно реализовывать 100 одинаковых последовательностей.

Приведенный выше код является очень грубым (не имеет нулевой проверки. Не имеет проверки типа класса объекта параметра и т. Д.) И не проверен, но я надеюсь, что вы поняли.

Еще одна вещь, на которую следует обратить внимание, это то, что вам также необходимо переопределить метод hash.Ниже приводится цитата из документации NSObject

If two objects are equal, they must have the same hash value. This last point is particularly important if you define isEqual: in a subclass and intend to put instances of that subclass into a collection. Make sure you also define hash in your subclass.
0 голосов
/ 12 февраля 2012

Размещение рабочего кода здесь на всякий случай, если это поможет другим - этот код учитывает, что один или оба объекта являются нулевыми (что не будет сравниваться при использовании isEqual):

синтезирует propertyNames в объектной модели: инициализировать один раз массивом массив строк с ProperyNames:

-(void)initPropertyNamesArray {
    propertyNames = [[NSArray alloc] initWithObjects:
                     @"lastName",
                     @"firstName",
                     @"dob",
                     @"notes", ..., nil];
}

Переопределить метод isEqual: (в этом примере сравниваются типы объектов, которые я использую в своей модели - строки, даты и изображения):

    - (BOOL) isEqual:(Patient *)otherPatient {
    if (!self.propertyNames) [self initPropertyNamesArray];
    BOOL showLog = YES;
    if (showLog) NSLog(@"Patient ISEQUAL");
    for(NSString *property in self.propertyNames) {
        id obj1 = [self valueForKey:property];
        id obj2 = [otherPatient valueForKey:property];
        if (showLog) NSLog(@"Comparing %@:", property);

         //CHECK IF ONE OR THE OTHER IS NULL -- the're not equal if so
        if (! (obj1 && obj2)) {
            if ((obj1 && !obj2) || (!obj1 && obj2)) {
                if (showLog) NSLog(@"%@ is null %@ is not!", obj1?@"TO:":@"FROM:", obj2?@"TO:":@"FROM:");
                return NO;
            }
        }

        //STRING
        if([obj1 isKindOfClass:[NSString class]]) {
            if(![obj2 isKindOfClass:[NSString class]])
                return NO;

            NSString * strObj1 = (NSString *)obj1;
            NSString * strObj2 = (NSString *)obj2;
            if (strObj1 && strObj2) { 
                if (![strObj1 isEqualToString:strObj2]) {
                    if (showLog) NSLog(@"%@ changed from:%@ to:%@", property, strObj1, strObj2);
                    return NO;
                }
            } else if (! (!strObj1 && !strObj2)) { //one is nil one isn't NOT EQUAL
                if (showLog) NSLog(@"%@ changed from:%@ to:%@", property, strObj1, strObj2);
                return NO; 
            } 
            //both nil (call them equal) 

        //DATE    
        } else if([obj1 isKindOfClass:[NSDate class]]) {
            if(![obj2 isKindOfClass:[NSDate class]])
                return NO;

            NSDate * dateObj1 = (NSDate *)obj1;
            NSDate * dateObj2 = (NSDate *)obj2;
            if (dateObj1 && dateObj2) { 
                if (![dateObj1 isEqualToDate:dateObj2]) {
                    if (showLog) NSLog(@"%@ changed from:%@ to:%@", property, dateObj1, dateObj2);
                    return NO;
                }
            } else if (! (!dateObj1 && !dateObj2)) { //one is nil one isn't NOT EQUAL
                if (showLog) NSLog(@"%@ changed from:%@ to:%@", property, dateObj1, dateObj2);
                return NO; 
            } 
            //both nil (call them equal)

        //IMAGE
        } else if([obj1 isKindOfClass:[UIImage class]] && ![property isEqual:@"lastUpdated"]) {
            if(![obj2 isKindOfClass:[UIImage class]])  
                return NO;

            UIImage * imgObj1 = (UIImage *)obj1;
            UIImage * imgObj2 = (UIImage *)obj2;
            if (imgObj1 && imgObj2) {  //check both not nil
                if(![imgObj1 isEqual:imgObj2]) {
                    if (showLog) NSLog(@"%@ changed from:%@ to:%@", property, imgObj1, imgObj2);
                    return NO;
                }
            } else if (! (!imgObj1 && !imgObj2)) { //one is nil one isn't NOT EQUAL
                if (showLog) NSLog(@"%@ changed from:%@ to:%@", property, imgObj1, imgObj2);
                return NO; 
            } 
            //both nil (call them equal)
        }
        if (showLog) NSLog(@"OK!");
    }
    if (showLog) NSLog(@"PATIENTS ARE THE SAME!");
    return YES;
}

Вы, очевидно, можете убрать все NSLogs, которые я вставил для отладки. Спасибо всем, кто помог мне!

0 голосов
/ 01 февраля 2012

Существует немного менее утомительный способ, основанный на valueForKey:, но у него есть свои собственные компромиссы производительности.

- (BOOL) isEqual: (id) obj
{
    if ([self class] != [obj class])
        return NO;

    NSArray *myProperties = ...;
    for (NSString *propertyName in myProperties) {
        if (![[self valueForKey:propertyName] isEqual: [obj valueForKey:propertyName]])
            return NO;
    }

    return YES;
}
0 голосов
/ 01 февраля 2012

isEqual: сравнивает указатели, то есть, если они не являются абсолютно одинаковыми объектами, они не будут равны.То, что вы хотите сделать, это использовать метод compare:, который фактически сравнит содержимое строк NSStrings.Взгляните на документацию , есть несколько опций, которые вы можете указать даже для поиска с учетом регистра или без учета регистра.NSDate имеет аналогичный метод сравнения, но вы также можете сделать:

– isEqualToDate:
– earlierDate:
– laterDate:

Взгляните на документацию по этому вопросу.

Редактировать:

У NSStrings также есть метод isEqualToString:, поэтому вы можете сделать:

[objectA isEqualToString:objectB]

, который будет возвращать YES или NO в зависимости от того, является ли Unicode для objectA и objectB одинаковым.Это было бы лучшим вариантом, если вы просто проверяли на равенство между точными копиями, если вы хотите провести сравнение на основе определенных параметров, таких как чувствительность к регистру compare: - лучший выбор.

...