问题
I have the following code fragment:
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
So if I have an object which is either an NSDictionary
or an NSMutableDictionary
, short of a try block (which I'd rather avoid), how can I tell the difference?
回答1:
NSAssert([bar isMemberOfClass: [NSMutableDictionary class]], @"");
回答2:
Honestly, you can't easily tell the difference without a @try
block. Standard practice is as follows:
- If you want to check whether it's mutable in order to avoid someone passing you an unexpectedly mutable object and then mutating it out from under you,
copy
the dictionary. If it's really non-mutable, the frameworks will optimize that into aretain
. If it really was mutable, then you probably wanted a copy anyway. - If you need a mutable dictionary, declare your method parameters to require one. Simple as that.
- In general, trust that people won't try to mutate objects unless specifically told they were mutable.
回答3:
It's pretty simple actually, all you need to do is test against the member of the class, like this:
if ([bar isMemberOfClass:[NSMutableDictionary class]]){
[bar setValue:value forKey: key];
}
iPhone SDK difference between isKindOfClass and isMemberOfClass
回答4:
If you need to guarantee that a dictionary is mutable, you should:
- create it as mutable if you are the one initializing it
- turn it into a mutable instance with the -[NSDictionary mutableCopy] method, for example if you're calling an API that returns an NSDictionary. Note that mutableCopy increments the retain count; you need to release this instance if you are using manual reference counting.
Determining whether an NSDictionary is mutable before attempting to modify its keys and/or values can be considered an example of a code smell, indicating that there's a deeper problem that should be solved. In other words, you should always know whether a given dictionary instance is NSDictionary or NSMutableDictionary. The code should be clear on that point.
回答5:
If you really don't care if it's mutable, but want to mutate it anyway, you could try this:
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]);
回答6:
I end up using @try catch but it only fires once. So it works like this
NSMutableDictionary *valuesByIdMutable = (id)self.valuesById;
simplest_block block = ^{
[valuesByIdMutable setObject:obj forKey:key];
};
@try {
block();
} @catch (NSException *exception) {
self.valuesById = valuesByIdMutable = [valuesByIdMutable mutableCopy];
block();
}
来源:https://stackoverflow.com/questions/9235965/ios-distinguish-an-nsdictionary-from-an-nsmutabledictionary