NSMutableSet removeObject работает не так, как ожидалось

#ios #objective-c #nsmutableset

#iOS #objective-c #nsmutableset

Вопрос:

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

Чтобы изолировать проблему, я закодировал быстрый тест, чтобы проиллюстрировать проблему (если таковая имеется):

Пример 1: работает так, как я ожидал

 NSMutableSet *colors = [[NSMutableSet alloc] init];
NSMutableString *color1 = [[NSMutableString alloc] initWithString:@"Blue"];
NSMutableString *color2 = [[NSMutableString alloc] initWithString:@"Green"];
[colors addObject:color1];
[colors addObject:color2];
NSLog(@"%@",[colors description]); // {( Green, Blue )}
[color1 insertString:@"Cobalt " atIndex:0]; 
NSLog(@"%@",[colors description]); // {( Green, "Cobalt Blue" )}
[colors removeObject:color1];
NSLog(@"%@",[colors description]); {( Green )}
  

Пример 2: работает НЕ так, как я ожидал

 NSMutableSet *surnames = [[NSMutableSet alloc] init];
NSMutableString *surname1 = [[NSMutableString alloc] initWithString:@"Brown"];
NSMutableString *surname2 = [[NSMutableString alloc] initWithString:@"Homes"];
[surnames addObject:surname1];
[surnames addObject:surname2];
NSLog(@"%@",[surnames description]); // {( Homes, Brown )}

[surname1 appendString:@"ie"];
NSLog(@"%@",[surnames description]); // {( Homes, Brownie )}
[surnames removeObject:surname1];
NSLog(@"%@",[surnames description]); // {( Homes, Brownie )}

NSString *surnameToRemove = nil;
for (NSString *surname in surnames) {
    if ([surname isEqualToString:@"Brownie"]) {
        surnameToRemove = surname;
        break;
    }
}
[surnames removeObject:surnameToRemove];
NSLog(@"%@",[surnames description]); // {( Homes, Brownie )}
  

Как показано в примере 2, после изменения он не удаляется surname1 , removeObject даже после получения ссылки путем ее поиска. Почему это происходит? Не могут ли изменяемые контейнеры содержать изменяемые объекты?

Я прочитал в этом посте, который NSSet кэширует хэши содержащихся объектов, и это может быть проблемой. Если да, есть ли способ его очистить? Есть альтернативное решение?

Просто ради любопытства, почему пример 1 работает?

Обновить:

Из коллекции Apple Темы программирования:

Если изменяемые объекты хранятся в наборе, либо метод хэша объектов не должен зависеть от внутреннего состояния изменяемых объектов, либо изменяемые объекты не должны изменяться, пока они находятся в наборе. Например, изменяемый словарь может быть помещен в набор, но вы не должны изменять его, пока он там. (Обратите внимание, что может быть трудно определить, находится ли данный объект в коллекции).

ОБНОВЛЕНИЕ 2:

Важно, пример 2 возвращает разные выходные данные журнала, если вы используете приложение для Mac или iOS:

Войдите в приложение Mac (работает так, как я ожидал):

 {( Green, Blue )}
{( Green, "Cobalt Blue" )}
{( Green )}
{( Brown, Homes )}
{( Brownie, Homes )}
{( Homes )}
-[__NSSetM removeObject:]: object cannot be nil
  

Войдите в приложение iOS (работает не так, как я ожидал):

 {( Green, Blue )}
{( Green, "Cobalt Blue" )}
{( Green )}
{( Homes, Brown )}
{( Homes, Brownie )}
{( Homes, Brownie )}
{( Homes, Brownie )}
  

ОБНОВЛЕНИЕ 3:

Тот же код, что и в примере 2, но с NSMutableArray , похоже, работает … так что угадайте, как NSMutableSet работает с хэшами. Я считаю, что, как указано в связанном потоке выше, он их кэширует:

 NSMutableArray *surnames = [[NSMutableArray alloc] init];
NSMutableString *surname1 = [[NSMutableString alloc] initWithString:@"Brown"];
NSMutableString *surname2 = [[NSMutableString alloc] initWithString:@"Homes"];
[surnames addObject:surname1];
[surnames addObject:surname2];
NSLog(@"%@",[surnames description]);  // {( Homes, Brown )}
[surname1 appendString:@"ie"];
NSLog(@"%@",[surnames description]);  // {( Homes, Brownie )}
[surnames removeObject:surname1];
NSLog(@"%@",[surnames description]);  // {( Homes )}
  

Комментарии:

1. Работает ли removeAllObjects() в примере 2?

2. Да, это работает. Есть обходной путь создания набора снова без surnameToRemove . Но я нахожу это излишним.

3. Вы можете сделать то же самое с NSMutableArray, а затем создать NSMutableSet с array…

4. Я не могу воспроизвести ваше поведение. Когда я запускаю пример 2 , третий NSLog() показывает, что Brownie был удален и Homes является единственным элементом в наборе.

5. Поймите, что набор будет следовать той же базовой схеме, что и ключевое значение словаря. Функции хэша и сравнения используются для размещения объекта в хэш-таблице. Если после его вставки в таблицу вы измените объект таким образом, чтобы изменить способ его хэширования или сравнения, объект будет недоступен для поиска.