如何从分层的 .plist 中获取值?

How do I get values from a tiered .plist?

I've already looked at Parse Plist (NSString) into NSDictionary and deemed it to be not a duplicate, as that question and its answer do not address my concerns.


我在文件系统中有一个 .plist 文件,结构如下:

.plist 文件的源代码如下所示:

{
    "My App" = {
        "Side Panel" = {
            Items = {
                Price = "#123ABC";
            };
        };
    };
}

我知道如何像这样在 Root 中获取项目:

[[NSBundle mainBundle] pathForResource:@"filename" ofType:@"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSString value = [dict objectForKey:@"key"]);

但是如果结构像我的一样,有分层字典呢? 如何获取 Price 的值?

我想用一种方法完成这一切,理想情况下是这样的:

通话中

NSString *hexString = [self getColorForKey:@"My App.Side Panel.Items.Price"];

定义

- (NSString *) getColorForKey: (NSString *)key
{
    NSArray *path = [key componentsSeparatedByString:@"."];
    NSDictionary *colors = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Colors" ofType:@"plist"]];
    NSString *color = @"#FFFFFF"; // white is our backup

    // What do I put here to get the color?

    return color;
}

这是对我有用的解决方案:

+ (NSString*) getHexColorForKey:(NSString*)key
{
    NSArray *path = [key componentsSeparatedByString:@"."];
    NSDictionary *colors = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Colors" ofType:@"plist"]];
    NSString *color = @"#FFFFFF";
    for (NSString *location in path) {
        NSObject *subdict = colors[location];
        if ([subdict isKindOfClass:[NSString class]])
        {
            color = (NSString*)subdict;
            break;
        }
        else if ([subdict isKindOfClass:[NSDictionary class]])
        {
            colors = (NSDictionary*)subdict; // if it's a dictinoary, our color may be inside it
        }
        else
        {
            [SilverLog level:SilverLogLevelError message:@"Unexpected type of dictinoary entry: %@", [subdict class]];
            return color;
        }
    }
    return color;
}

其中 key 是匹配 /^[^.]+(\.[^.]+)*$/NSString,这意味着它看起来像我的目标 @"My App.Side Panel.Items.Price".

是的,我明白你想要完成什么;谢谢你的澄清。不过,我要补充一点,我所写的资源和建议确实提供了解决您问题的必要信息。

就是说,下面是你的字典:

NSURL *plistURL = [[NSBundle mainBundle] URLForResource:@"Info" withExtension:@"plist"];
NSData *plistData = [NSData dataWithContentsOfURL:plistURL];
NSDictionary *tieredPlistData = [NSPropertyListSerialization propertyListWithData:plistData 
                                                                          options:kCFPropertyListImmutable 
                                                                           format:NULL 
                                                                            error:nil];

那么,如果我们对Items

中包含的信息感兴趣
NSDictionary *allItemsDictionary = tieredPlistData[@"My App"][@"Side Panel"][@"Items"];

假设 Items 将包含多个对象,您可以使用

NSArray *keys = [allItems allKeys];
for(NSString *key in keys){
    NSString *colorValue = allItemsDictionary[key];
    // do something with said color value and key
}

或者,如果您需要一个值,则只需引用该键

NSString *colorForPriceText = allItemsDictionary[@"Price"];

但有几点提示:

  • 通常认为将经常访问的值保存在代码中而不是在运行时加载的 plist/file 是更好的主意。
  • 就是说,您不会将调用从 NSBundle 加载到与查询特定值相同的方法中。在您的示例中,每次需要一种颜色时,您最终都会重新访问 NSBundle 并堆积不需要的内存分配。一种方法是将 plist 加载到 iVar NSDictionary 中,然后 NSDictionary 将由另一种方法单独使用。