2013-10-24 3 views
3

Таким образом, документация Apple, говорит, что это о containsObject:NSArray containsObject не работает как рекламируется

Этот метод определяет anObject присутствует в массиве с помощью отправки IsEqual в: сообщение для каждого из объектов массива (и передавая anObject в качестве параметра для каждого isEqual: message).

Вы можете найти эти документы here.

Однако, я испытываю почти полный противоположный эффект. Вместо отправки isEqual: каждому объекту в моем массиве объект, который я предоставляю, получает сообщение isEqual: для каждого объекта в массиве.

Например, у меня есть два класса: FlxSort и FlxFieldKey. Класс FlxSort содержит ключ поля, и я переопределил isEqual:, чтобы вернуть true, если он передал объект FlxFieldKey, который соответствует ключу, который он удерживает. Теоретически это должно позволить мне проверить, присутствует ли в массиве объект FlxSort с определенным ключом. Однако FlxFieldKey не является (и не должен) знать объект FlxSort, поэтому он всегда будет возвращать значение false, если в сообщении FlxSort содержится сообщение isEqual. Поэтому в приведенном ниже коде я ожидаю, что каждый объект в currentSorts будет отправлен сообщение isEqual: для каждого ключа в _avialableKeys. Вместо этого каждый ключ в _availableKeys отправляется сообщение isEqual:. Я проверил это как с протоколированием, так и с точками останова.

NSMutableArray *keys = [NSMutableArray arrayWithCapacity:_avialableKeys.count]; 
NSArray *currentSorts = _sortGroup.sorts; 
for (FlxFieldKey *key in _avialableKeys){ 
    if (![currentSorts containsObject:key]){ 
     [keys addObject:key]; 
    } 
} 

Я пропустил что-то здесь?

Update
Хотя я спрашивал о [NSArray containsObject:] я получил много откликов о моей реализации асимметричного равенства. Поэтому я хочу уточнить, что моя реализация была экспериментом, рожденным из любопытства. Исходный код не использовал containsObject вообще:

NSMutableArray *keys = [NSMutableArray arrayWithCapacity:_avialableKeys.count]; 
NSArray *currentSorts = _sortGroup.sorts; 
for (FlxFieldKey *key in _avialableKeys){ 
    BOOL found = NO; 
    for (FlxSort *sort in currentSorts){ 
     if ([sort.key isEqual:key]){ 
      found = YES; 
      break; 
     } 
    } 
    if (!found){ 
     [keys addObject:key]; 
    } 
} 

мне было интересно, если я мог бы вышка containsObject работать в описанной выше ситуации. Конечно, как было отмечено несколькими людьми, создание асимметричного равенства - плохая идея и идет прямо против рекомендаций Apple. I может изменить метод isEqual: в FlxFieldKey для учета FlxSort объектов, которые восстановили бы симметричное равенство между двумя классами ... вид. Вы все равно можете получить A == B, A == C, но B! = C. И это все еще оставляет проблему hash (они должны быть одинаковыми).

Повторю: Вы не хотите этого делать (асимметричное равенство). По крайней мере, не в производственном кодексе. Я просто возился здесь, видя, что я может do, не то, что я должно сделать. Я признаю, что мне было безответственно не уточнять это в первоначальном вопросе, поскольку менее опытные разработчики могут подумать, что такая реализация вполне оправдана. Прошу прощения за это.

И да, я, вероятно, сообщить об ошибке в документации Radar, как только я могу набраться смелости, чтобы войти в Apple, ошибка репортер снова ...

+0

Возможно, вы должны сделать это в своем собственном цикле и с помощью собственного метода сравнения. Поскольку вы полагаетесь на детали реализации Apple ... – Wain

+0

Да, я уже работал над этим с помощью своего собственного цикла, но мне потребовалось немного времени, чтобы выяснить, что происходит. Я просто подумал, что было бы безопасно предположить, что это сработало так, как они сказали, что это сработало. Я задал вопрос, потому что хочу убедиться, что я чего-то не пропущу. –

+4

Это невероятно опасно. Вы создали ситуацию, когда A == B, но B! = A. Это неопределенное поведение для многих алгоритмов. Вы должны найти другой подход. –

ответ

0

Вы правы, это несоответствие между реализацией и документацией, и вы должны file a bug on radar.

Однако он служил для выделения проблемы с дизайном в вашей реализации.

Порядок сравнения действительно не имеет значения, как равенство

[objectA isEqual:objectB] == [objectB isEqual:objectA]; 

всегда должно быть правдой, если вы намеренно не хотите нарваться на неприятности.

Это сказанное вами, я считаю, что NSSet будет гораздо более подходящим для вида задачи, так как containsObject: операция выполняется в постоянном времени, в отличие от линейной в случае NSArray. В таком случае вам также необходимо переопределить метод hash.

1

Я не знаю, что это то, что на самом деле происходит, но containsObject: реализации, как (слабо):

for(id containedObj in self){ 
    if([testObj isEqual:containedObj]){ 
     return YES; 
    } 
} 

testObj означает, что «ы isEqual: реализация может быть кэширован и вызывается непосредственно, а не причем это сообщение послано через objc_msgSend() каждый раз. Поскольку NSArray s может быть гетерогенным, было бы невозможно кэшировать каждый вызов, если бы сравнение было наоборот, как говорят документы.

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

Возможно, вы должны зарегистрировать ошибку документа, хотя, как заметил Роб Напир, это проблема только в том случае, если вы делаете равенство асимметричным, что само по себе является проблематичной идеей.

Смежные вопросы