【问题标题】:Access NSDictionary via dot notation?通过点符号访问 NSDictionary?
【发布时间】:2026-02-06 13:40:01
【问题描述】:

有没有办法像这样通过点符号访问 NSDictionary 中的键值?

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

【问题讨论】:

  • 给我几分钟,我会做一些东西,让你使用点符号使用类别。
  • @RichardJ.RossIII 我认为您不应该用类别来回答。它展示了一些 Obj-C 功能,但这样做是非常糟糕的做法。也许建议一个作为NSObject 子类的模型对象会是更好的方法。
  • 我怀疑您是否可以通过覆盖 - (id)valueForUndefinedKey: 来做一些事情,因为编译器会拒绝点...
  • 这不是问题,理查德,我只是好奇我是否遗漏了什么,我可以使用 object/valueForKey。
  • @fuzzygoat 都一样,有解决办法,如果你看到我的回答...

标签: iphone objective-c nsdictionary


【解决方案1】:

在 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"
}

【讨论】:

    【解决方案2】:

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

    myDictionary[@"key"]
    

    而不是

    [myDictionary objectForKey:@"key"]
    

    【讨论】:

      【解决方案3】:

      我同意大多数应使用objectForKey: 或类似方法访问NSDictionary 的答案。但是,可以允许点符号访问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 的启发。主要区别在于该解决方案基于子分类而不是类别。

      【讨论】:

      【解决方案4】:

      NSDictionary 没有点语法,但应考虑使用objectForKey: 而不是valueForKey:

      Difference between objectForKey and valueForKey?

      【讨论】:

      • 谢谢,我会这样做,我想知道有什么区别。感谢您的链接。
      • +1 这个答案比我的答案更简洁,所以我将删除我的答案。
      • @fuzzygoat 没问题,希望对您有所帮助。
      【解决方案5】:

      不是真的,不。

      点表示法是调用具有该选择器名称的方法的简写方式。也就是说,这……

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

      ...和这个一样...

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

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

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

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

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

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

      【讨论】:

        【解决方案6】:

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

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

        我不知道这是否是您正在寻找的,但我希望它有所帮助!

        【讨论】:

        • 我不明白为什么有人会诚实地否决这个。它回答了 OP 的问题,我如何使用点符号访问字典。这允许它。
        • 这很有趣,但它与@fuzzygoat 的问题有什么关系?
        • @benzado 因为它允许他使用带有点符号的 NSDictionary,这正是他想要的?
        • 是的,您正在访问字典并且您正在使用点表示法,但您并没有真正使用点表示法来访问字典中的键。
        • 感谢您抽出宝贵时间理查德。我认为答案是没有一个简单的方法(即dictionary.key),所以现在我要换成使用objectForKey。再次非常感谢...
        【解决方案7】:

        不,你的做法是正确的。在 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);
        

        【讨论】:

        • 技术上不是。你可以用块和类别做一些非常疯狂的事情,看看我的答案。
        【解决方案8】:

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

        NSArray *allValues = [returnVal allValues];
        

        现在也可以使用这个数组来执行任务。 如果你想要一些具体的东西,那就提一下,可能是因为有其他方法。

        同样由于 NSDictionary 类不会定义任何属性,所以点符号直接是不可能的。

        【讨论】:

          【解决方案9】:

          不,我不这么认为。

          来自参考手册。

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

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

          如果密钥是公共属性并且可读,您将能够访问它。

          【讨论】: