问题
Is there a way via dot notation to access the values of keys in an NSDictionary like this?
NSDictionary *returnVal = [NSDictionary dictionaryWithObjectsAndKeys:@"Saturn", @"name", @"Gas Giant", @"type", nil];
NSLog(@"VALUE: %@", [returnVal valueForKey:@"name"]); // This is how I am doing it now.
回答1:
There is no dot syntax for NSDictionary
, but should consider using objectForKey:
instead of valueForKey:
Difference between objectForKey and valueForKey?
回答2:
Not really, no.
The dot notation is a shorthand way of calling a method with that selector name. In other words, this...
NSLog(@"Hello, %@", foo.bar.name);
...is the same as this...
NSLog(@"Hello, %@", [[foo bar] name]);
When I say "same", I mean they are compiled down to the same code. It's just syntactic sugar.
A plain NSDictionary won't act that way. You could sort of fake it with Key Value Coding, which lets you call valueForKeyPath
to get properties like this:
NSLog(@"Hello, %@", [foo valueForKeyPath:@"bar.name"]);
If you really wanted to be able to write foo.bar.name
in your code, however, you'd have to make a custom class that overrides forwardInvocation:
; this lets you catch an unknown message to an object and do something else with it besides throw an error. In this case, you could change the unknown selector to a lookup on an NSDictionary instance it contains.
But even if you did that, the compiler would probably still generate warnings unless you made header files that declared those property names to exist.
回答3:
I agree with most of the answers that NSDictionary
should be accessed with objectForKey:
or similar methods. However it is possible to allow for dot notation access to a NSDictionary
, and for learning purposes this might be interesting for someone. Also when for example your are retrieving large JSON dictionaries via AFNetworking
, this method can ease the access and readability of your code.
This is my solution:
DictionaryProperties.h: (class wrapping the NSDictionary for property access)
@interface DictionaryProperties : NSObject{
NSMutableDictionary* _backingDict;
}
@property (nonatomic, strong) NSMutableDictionary* backingDict;
+ (DictionaryProperties*) allocWithDictionary:(NSDictionary*)dict;
@end
DictionaryProperties.m:
#import "DictionaryProperties.h"
@implementation DictionaryProperties
@synthesize backingDict = _backingDict;
- (id) initWithDictionary:(NSDictionary*)dict {
if (self) {
if ([dict isKindOfClass:[NSMutableDictionary class]]) {
self.backingDict = (id)dict;
} else {
self.backingDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
}
}
return self;
}
+ (DictionaryProperties*) allocWithDictionary:(NSDictionary*)dict {
return [[DictionaryProperties alloc] initWithDictionary:dict];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
NSString* key = NSStringFromSelector(invocation.selector);
invocation.selector = @selector(objectForKey:);
[invocation setArgument:&key atIndex:2];
if ([self.backingDict objectForKey:key]) {
[invocation invokeWithTarget:self.backingDict];
} else {
[self doesNotRecognizeSelector:invocation.selector];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [self.backingDict methodSignatureForSelector:@selector(objectForKey:)];
}
@end
ExampleDictContent.h: (class declaring what is inside the dictionary)
#import "DictionaryProperties.h"
@interface ExampleDictContent : DictionaryProperties
@property (strong, nonatomic) NSString* someData;
@property (strong, nonatomic) NSString* someOtherData;
@end
@implementation ExampleDictContent
@end
Usage: (simple declaration of a dictionary, allocation of wrapper and property access)
#import "ExampleDictContent.h"
NSDictionary* d = [NSDictionary dictionaryWithObjects:NSArray arrayWithObjects:@"someData content", @"someOtherData content", nil
forKeys:NSArray arrayWithObjects:@"someData", @"someOtherData", nil];
ExampleDictContent* dictWProps = [ExampleDictContent allocWithDictionary:d];
NSLog(dictWProps.someData);
NSLog(dictWProps.someData);
This will print:
someData content
someOtherData content
So basically DictionaryProperties
works as a facade for accessing the NSDictionary
. It uses forwardInvocation
to convert a get-property
method call into a getObjectForKey:
call on the dictionary. What I like about it, is that it allows for autocompletion on the dictionary, and also allows me to explicitly declare what keys I want to access (in the ExampleDictContent.h
file). Note that this solution does not allow for write access to the properties, but that can be added as shown in the link below.
This solution has partly been inspired by karstenlitsche's solution. The main difference is that this solution is based on sub-classing instead of categories.
回答4:
No, I don't think so.
From the reference manual.
Accessing Keys and Values
– allKeys
– allKeysForObject:
– allValues
– getObjects:andKeys:
– objectForKey:
– objectsForKeys:notFoundMarker:
– valueForKey:
That's listed as the only way to access the keys and the values. So you are doing it alright.
You would be able to access it if the keys were a public property and it was readable.
回答5:
The way that you have mentioned for accessing element of dictionary is ideal way(using keys). If you want to do something else, might be you can use-
NSArray *allValues = [returnVal allValues];
Now using this array as well you can perform tasks. And if you want something specific then mention that, might be for that there can be some other way.
Also as NSDictionary class won't have any property defined, so dot notation is directly not possible.
回答6:
No, you are doing it the correct way. In the iOS world, often the correct way is the only way. :)
If you really want dot notation (and other nice things you get with typed objects), you're going to have to stuff the dictionary representation into an object. Most commonly my interface will look like:
@interface FooBar : NSObject {
NSString *someData;
int someNumber;
}
@property (nonatomic, copy) NSString *someData;
@property (nonatomic, assign) int someNumber;
+ (FooBar *)FooBarFromDictionary:(NSDictionary *)dataDict;
@end
The implementation should be clear. Then you can
FooBar *fb = [FooBar FooBarFromDictionary:data];
NSLog(@"fb.someData = %@", fb.someData);
回答7:
Technically, you can do something like this:
typedef id (^valueBlock)(id);
@interface NSDictionary(dotNotationAddons)
@property(nonatomic, readonly) valueBlock value;
@end
@implementation NSDictionary(dotNotationAddons)
-(valueBlock) value
{
return [[^(id key) {
return [self objectForKey:key];
} copy] autorelease];
}
@end
int main (int argc, const char * argv[])
{
@autoreleasepool {
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"1", @"One", @"2", @"Two", @"3", @"Three", @"4", @"Four", nil];
id value = dictionary.value(@"One");
NSLog(@"%@", value);
}
return 0;
}
I don't know if that is what you were looking for, but I hope it helps!
回答8:
The answer's still no, but you can use the shorthand
myDictionary[@"key"]
instead of
[myDictionary objectForKey:@"key"]
来源:https://stackoverflow.com/questions/9264466/access-nsdictionary-via-dot-notation