通过点表示法访问NSDictionary?

时间:2012-02-13 16:51:54

标签: iphone objective-c nsdictionary

有没有办法通过点表示法来访问像这样的NSDictionary中的键值?

NSDictionary *returnVal = [NSDictionary dictionaryWithObjectsAndKeys:@"Saturn", @"name", @"Gas Giant", @"type", nil];
NSLog(@"VALUE: %@", [returnVal valueForKey:@"name"]); // This is how I am doing it now.

9 个答案:

答案 0 :(得分:6)

NSDictionary没有点语法,但应考虑使用objectForKey:代替valueForKey:

Difference between objectForKey and valueForKey?

答案 1 :(得分:5)

不是,不。

点表示法是使用该选择器名称调用方法的简便方法。换句话说,这......

NSLog(@"Hello, %@", foo.bar.name);

......与此相同......

NSLog(@"Hello, %@", [[foo bar] name]);

当我说“相同”时,我的意思是它们被编译成相同的代码。这只是语法糖。

普通的NSDictionary不会这样做。您可以使用键值编码来伪造它,这可以让您调用valueForKeyPath来获取这样的属性:

NSLog(@"Hello, %@", [foo valueForKeyPath:@"bar.name"]);

但是,如果您真的希望能够在代码中编写foo.bar.name,则必须创建一个覆盖forwardInvocation:的自定义类;这可以让你捕获一个未知的消息给一个对象,并用它做其他事情,除了抛出一个错误。在这种情况下,您可以将未知选择器更改为它包含的NSDictionary实例上的查找。

但即使您这样做,编译器仍可能会生成警告,除非您创建了声明这些属性名称的头文件。

答案 2 :(得分:3)

我同意大多数应使用NSDictionary或类似方法访问objectForKey:的答案。但是,可以允许点符号访问NSDictionary,并且出于学习目的,这可能对某人有意义。此外,当您通过AFNetworking检索大型JSON词典时,此方法可以简化代码的访问和可读性。

这是我的解决方案:

DictionaryProperties.h :(包装NSDictionary以进行属性访问的类)

@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 :(类声明字典内的内容)

#import "DictionaryProperties.h"

@interface ExampleDictContent : DictionaryProperties

@property (strong, nonatomic) NSString* someData;
@property (strong, nonatomic) NSString* someOtherData;

@end

@implementation ExampleDictContent
@end

用法:(简单声明字典,分配包装和属性访问)

#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);

这将打印:

someData content
someOtherData content

所以基本上DictionaryProperties作为访问NSDictionary的外观。它使用forwardInvocationget-property方法调用转换为字典上的getObjectForKey:调用。我喜欢它的是,它允许在字典上自动完成,并且还允许我显式声明我想要访问的键(在ExampleDictContent.h文件中)。请注意,此解决方案不允许对属性进行写入访问,但可以添加,如下面的链接所示。

此解决方案部分受到karstenlitsche's solution的启发。主要区别在于此解决方案基于子类而不是类别。

答案 3 :(得分:1)

不,你这样做是正确的。在iOS世界中,通常正确的方法是唯一的方法。 :)

如果你真的想要点符号(以及你用类型对象获得的其他好东西),你将不得不将字典表示填充到一个对象中。最常见的是我的界面如下:

@interface FooBar : NSObject {
    NSString *someData;
    int someNumber;
}

@property (nonatomic, copy) NSString *someData;
@property (nonatomic, assign) int someNumber;

+ (FooBar *)FooBarFromDictionary:(NSDictionary *)dataDict;

@end

实施应该清楚。那你可以

FooBar *fb = [FooBar FooBarFromDictionary:data];
NSLog(@"fb.someData = %@", fb.someData);

答案 4 :(得分:1)

不,我不这么认为。

参考手册。

Accessing Keys and Values
– allKeys
– allKeysForObject:
– allValues
– getObjects:andKeys:
– objectForKey:
– objectsForKeys:notFoundMarker:
– valueForKey:

这被列为访问密钥和值的唯一方法。所以你做得很好。

如果密钥是公共属性且可读,则可以访问它。

答案 5 :(得分:1)

您提到的访问字典元素的方式是理想的方法(使用键)。 如果你想做别的事,你可以使用 -

NSArray *allValues = [returnVal allValues];

现在使用此数组也可以执行任务。 如果你想要特定的东西然后提一下,可能是因为可以有其他方式。

同样由于NSDictionary类没有定义任何属性,因此点符号是不可能的。

答案 6 :(得分:0)

从技术上讲,你可以这样做:

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;
}

我不知道这是不是你想要的,但我希望它有所帮助!

答案 7 :(得分:0)

答案仍然没有,但是您可以使用速记

myDictionary[@"key"]

代替

[myDictionary objectForKey:@"key"]

答案 8 :(得分:0)

在Swift中,有一个解决方案看似不太优雅,但却可以解决问题。

对于每种特定类型的Dictionary,它都需要一个typeAlias;对于字典中的每个预期键,都需要一个带有变量的扩展名(带有getter / setter)。根本不是一个好习惯

以相同的方式将dict对象包装到对象(类/结构)中可能会更容易。

typealias MyDict = [String:AnyObject]
extension MyDict {
    var key: AnyObject? {
        get { return self["key"] }
        set { self["key"] = newValue }
    }
}


// Usage
var myDict = MyDict()

// Get the value
myDict["key"] = "value1" as AnyObject
if let str = myDict.key {
    print(str)  // prints "value1"
}

// Set the value
myDict.key = "value2" as AnyObject
if let str = myDict["key"] {
    print(str)  // prints "value2"
}