2012-02-10 35 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 

所以,如果我有一个对象,或者是一个NSDictionaryNSMutableDictionary,短try块(我宁愿避免)的,我怎么能告诉区别?

+1

你可能想要使用-setObject:forKey:,而不是-setValue:forKey :. – 2012-02-10 23:06:35

+0

你有一个真实世界的例子,你什么时候有一本字典,但不确定它是否可变? – bneely 2012-02-10 23:15:45

+0

只想添加[bar respondsToSelector:@selector(setValue:forKey :)]不起作用。 – picciano 2012-02-10 23:32:47

回答

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

适用于iOS 6+。在iOS 5.x及更低版本中失败严重。在使用时请记住这一点。 – 2012-10-25 04:22:13

+0

只是抬起头来,如果你在主体内部,这将失败。改用NSCAssert。 – smileBot 2013-04-19 19:23:40

+0

这实际上是不正确的。 https://www.bignerdranch.com/blog/about-mutability/ – 2015-05-05 15:22:20

1

这其实很简单,所有你需要做的是对类的成员测试,这样的:

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

iPhone SDK difference between isKindOfClass and isMemberOfClass

+0

不幸的是,这实际上并不奏效。这是一个类集群,你可以在这里获得'__NSCFDictionary',正如OP指出的那样,这个测试失败了。 – 2012-02-10 23:15:26

+0

正如我指出的链接所显示的,OP作为一个例子写的内容和我写的内容之间有区别。 OP使用'isKindOfClass:'检查类的相等性,在这里我检查'isMemberOfClass:'。虽然我没有测试过,但如果bar是一个'NSDictionary',它应该是'false'。看到这个链接更好的解释:http://stackoverflow.com/a/3653948/267892 – Emil 2012-02-10 23:25:50

+0

然后,也许你应该测试它。 :)它不起作用 - 成员测试失败NSDictionary和NSMutableDictionary,因为既不是被检查的对象的实际类。 – 2012-02-11 05:36:49

5

老实说,你不能轻易分辨出来没有@try区块。标准的做法如下:

  • 如果你想检查它是否是为了避免有人通过可变你意想不到的可变对象,然后从下你,copy字典变异出来。如果它真的是不可变的,框架将优化成retain。如果它真的可变的,那么你可能想要一个副本。
  • 如果您需要一个可变字典,请声明您的方法参数需要一个。就那么简单。
  • 一般来说,相信人们不会试图改变对象,除非明确告诉他们是可变的。
0

如果你需要保证一个字典是可变的,你应该:

  • 创建为可变的,如果你是一个初始化它
  • 把它变成一个可变的实例与 - [NSDictionary mutableCopy]方法,例如,如果您正在调用返回NSDictionary的API。请注意,mutableCopy递增保留计数;如果使用手动引用计数,则需要释放此实例。

确定一个NSDictionary是否试图修改它的键和/或值之前,可变可以考虑code smell的例子,表明有应解决的深层次问题。换句话说,您应该始终知道给定的字典实例是NSDictionary还是NSM​​utableDictionary。代码应该清楚。

+0

理论上不错,但实际上这些情况确实发生。例如,假设我通过在某个数组上调用objectAtIndex:或者在某个字典上使用valueForKey:来获取它。现在你可能会说我应该小心我输入数组的内容。这是一个很好的理论,但因为我不想包含这些检查而导致崩溃的代码不是一种选择。 – 2012-02-11 01:30:05

+0

你不能总是知道。例如,在编写单元测试时,即使您确实知道,但验证方法返回的对象是否符合预期仍然是一个好主意。 – Caleb 2015-07-28 05:33:34

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(); 
} 
相关问题