Преобразуя NSArray в NSSet, экземпляры пользовательских классов передаются непоследовательно - PullRequest
7 голосов
/ 20 февраля 2012

Столкнулся с небольшой интересной проблемой.Я писал метод фильтрации массива по уникальным объектам:

- (NSArray*)distinctObjectsByAddress {
    NSSet* uniqueSet = [NSSet setWithArray:self];
    NSArray* retArray = [uniqueSet allObjects];

    return retArray;
}

и написал модульный тест для проверки:

- (void)testDistinctObjectsByAddress5 {
    Person* adam1 = [[Person alloc] initWithFirstName:@"adam" lastName:@"adam" andParent:nil];
    Person* adam2 = [[Person alloc] initWithFirstName:@"adam" lastName:@"adam" andParent:nil];

    testPersonArray = [NSArray arrayWithObjects:adam1,adam2, nil];

    NSArray* checkArray = [testPersonArray distinctObjectsByAddress];

    STAssertEquals([checkArray count], [testPersonArray count], @"Array %@ counts should match %@ %@",checkArray,adam1,adam2);
}

Довольно просто.Интересно то, что примерно в 80-90% случаев тест проходит успешно и очень часто он не проходит, потому что метод distinctObjectsByAddress возвращает только один объект.Мне удалось отследить его до вызова [NSSet setWithArray:self], но я также смог проверить, что два объекта лица - это два разных объекта (по крайней мере, у них разные адреса).Я предполагаю, что setWithArray: просто выполняет сравнение базовых адресов, но я не понимаю, почему иногда он создает два объекта, как это должно быть, а иногда только один.

То, что я только что попробовал, меняло adam2, чтобы имя и фамилия не были точно такими же, как adam1.Это, кажется, исправить ошибку.Указывает ли это на какую-то оптимизацию компилятора, когда объекты логически совпадают?

Ответы [ 2 ]

10 голосов
/ 20 февраля 2012

Я предполагаю, что setWithArray просто выполняет сравнение базовых адресов

Это неправильно. NSSet использует методы -isEqual: и -hash для объектов, которые к нему добавляются. Это зависит от того, как они реализованы в Person или его суперклассах.

Если [person1 isEqual:person2], то вы ожидаете, что набор будет содержать один объект. Если нет, то набор должен содержать два объекта.

Я предполагаю, что Person не следует правилам в его -isEqual: и -hash методах. Скорее всего, два объекта равны, но их хэши не равны, как они должны быть. (За исключением 10-20% случаев, когда вам везет.)

Указывает ли это на какую-то оптимизацию компилятора, когда объекты логически совпадают?

Нет, не существует оптимизации компилятора, которая бы объединяла два объекта в один.

3 голосов
/ 20 февраля 2012

Скорее всего, вы не реализовали hash для Person, а иногда идентичные Person объекты хэшируются в два разных сегмента.

...