2012-02-10 4 views
2

я следующий фрагмент кода:iOS - отличить NSDictionary от NSMutableDictionary?

NSString* value = @"value"; 
NSString* key = @"key"; 
NSMutableDictionary* foo = [NSMutableDictionary dictionary]; 
NSDictionary* bar = [NSDictionary dictionary]; 
NSAssert([bar isKindOfClass: [NSMutableDictionary class]], @""); // passes the assert as both are NSCFDictionary; 
NSAssert([bar respondsToSelector: @selector(setValue:forKey:)], @""); // passes the assert because it NSCFDictionary does respond to the selector 
[foo setValue:value forKey:key]; // no problem, foo is mutable 
[bar setValue:value forKey:key]; // crash 

Так что, если у меня есть объект, который является либо NSDictionary или NSMutableDictionary, если не считать попытку блока (который я предпочел бы избежать), как я могу рассказать разница?

+1

Возможно, вы захотите использовать -setObject: forKey :, not -setValue: forKey :. –

+0

У вас есть реальный пример того, когда у вас будет словарь, и не быть уверенным, является ли он изменчивым? – bneely

+0

Просто хотел добавить, что [бар отвечаетToSelector: @selector (setValue: forKey :)) НЕ работает. – picciano

ответ

3
NSAssert([bar isMemberOfClass: [NSMutableDictionary class]], @""); 
+2

Работает в iOS 6+. Ошибка в iOS 5.x и ниже. Помните об этом при использовании. –

+0

Только хедз-ап, если вы внутри основного, это не удастся. Вместо этого используйте NSCAssert. – smileBot

+0

Это действительно неверно. https://www.bignerdranch.com/blog/about-mutability/ –

1

Это очень просто на самом деле, все, что вам нужно сделать, это испытание против члена класса, как это:

if ([bar isMemberOfClass:[NSMutableDictionary class]]){ 
    [bar setValue:value forKey: key]; 
} 

iPhone SDK difference between isKindOfClass and isMemberOfClass

+0

На самом деле это не работает.Это кластер классов, и вы можете получить '__NSCFDictionary' здесь, как указывает OP, что не дает этого теста. –

+0

Как ссылка, которую я указываю на показы, есть разница между тем, что написал OP как пример и что я написал. OP проверяет равенство класса с 'isKindOfClass:', здесь я проверяю с помощью 'isMemberOfClass:'. Хотя я не тестировал его, он должен быть «false», если bar является «NSDictionary». См. Эту ссылку для лучшего объяснения: http://stackoverflow.com/a/3653948/267892 – Emil

+0

Тогда, возможно, вы должны ее протестировать. :) Это не работает - тест членства терпит неудачу как для NSDictionary, так и для NSMutableDictionary, так как ни один из них не является фактическим классом исследуемого объекта. –

5

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

  • Если вы хотите проверить, является ли это изменчиво для того, чтобы избежать кто-то мимо вас неожиданно изменяемый объект, а затем мутирует его из-под вас, copy словаря. Если это действительно не изменяемо, рамки оптимизируют это значение в retain. Если это действительно было mutable, то вы, вероятно, захотели получить копию.
  • Если вам нужен изменяемый словарь, объявите параметры своего метода, чтобы их требовать. Просто как тот.
  • В общем, полагайте, что люди не будут пытаться мутировать объекты, если только они не сказали, что они изменяемы.
0

Если вам нужно, чтобы гарантировать, что словарь является изменяемым, вы должны:

  • создать его как изменяемый, если ты один инициализацию
  • превратить его в изменяемом например, с метод [NSDictionary mutableCopy], например, если вы вызываете API, который возвращает NSDictionary. Обратите внимание, что mutableCopy увеличивает счетчик сохранения; вам нужно освободить этот экземпляр, если вы используете ручной подсчет ссылок.

Определение ли NSDictionary является изменяемым, прежде чем пытаться изменить свои ключи и/или значения можно считать примером code smell, указывая, что есть более глубокая проблема, которая должна быть решена. Другими словами, вы всегда должны знать, является ли данный экземпляр словаря NSDictionary или NSMutableDictionary. На этом этапе код должен быть ясным.

+0

Ницца в теории, но на практике эти ситуации происходят. Например, предположим, что я получил его, вызвав objectAtIndex: на некоторый массив или valueForKey: в каком-нибудь словаре. Теперь вы можете сказать, что я должен быть осторожным в том, что я ввел в массив. Это прекрасная теория, но код доставки, который разбивается просто потому, что я не хочу включать эти проверки, не является вариантом. –

+0

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

0

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

NSString* value = @"value"; 
NSString* key = @"key"; 

NSDictionary* bar = [NSDictionary dictionary]; 
NSLog(@"bar desc: %@", [bar description]); 
NSMutableDictionary* temp = [bar mutableCopy]; 
[temp setValue:value forKey:key]; 
bar = temp; 
NSLog(@"bar desc: %@", [bar description]); 
0

Я в конечном итоге с помощью @try поймать, но только один раз срабатывает. Так оно работает

NSMutableDictionary *valuesByIdMutable = (id)self.valuesById; 

simplest_block block = ^{ 
    [valuesByIdMutable setObject:obj forKey:key]; 
}; 

@try { 
    block(); 
} @catch (NSException *exception) { 
    self.valuesById = valuesByIdMutable = [valuesByIdMutable mutableCopy]; 
    block(); 
}