【问题标题】:Key-Value Coding Get Element in List键值编码获取列表中的元素
【发布时间】:2013-08-22 18:05:19
【问题描述】:

我有两个对象:

@interface AObject : NSObject

@property NSArray *bObjects;

@end

 

@interface BObject : NSObject

@property NSString *name;

@end

AObject 的实例上使用键值编码,我可以得到bObjects 的列表(@"self.bObjects")和bObjects 的名称列表(@"self.bObjects.name")。

但是,我想要的只是bObjects 的第一个名称。我的直觉是键值编码应该支持列表下标,例如:@"bObjects[0].name"

但这似乎并不存在。如何获得单个实体; AObject 的第一个 BObject 的名称,使用键值编码?

脚注:我在上一个问题中意识到我愚蠢地将 NSPredicate 和 KV 编码混为一谈。

【问题讨论】:

  • 你没有。 KVC 不支持访问集合中的单个元素。如果你需要这种支持,你应该打开一个雷达。
  • @RobNapier,你可以作弊并使用firstObject作为密钥吗?
  • 我不确定你可以。如果我们假设这是可能的,那么在尝试访问给定索引处的非现有对象时会遇到麻烦(NSOutOfBoundsException)。
  • @Wain:我刚试过这个。问题是数组上的valueForKey: 立即将键应用于所有数组元素。所以[aObject valueForKeyPath:@"bObjects.firstObject.name"] 尝试将firstObject 应用于bObjects 数组中的所有元素,而不是应用于数组本身。 - 我从来没有找到避免这种情况的方法。
  • 对于这种特殊情况,我能想到的唯一解决方案是将属性“firstBObject”添加到AObject 类。这行得通,但不是很通用。

标签: ios objective-c key-value-coding


【解决方案1】:

正如 Martin R 在 cmets 中提到的,目前最好的选择是在 AObject 类中创建一个 firstBObject 属性。

AObject.h/m

@class BObject;

@interface AObject : NSObject
+ (AObject*)aObjectWithBObjects:(NSArray*)bObjects;
@property NSArray *bObjects;
@property (nonatomic, readonly) BObject *firstBObject;
@end

@implementation AObject
+ (AObject*)aObjectWithBObjects:(NSArray*)bObjects
{
    AObject *ao = [[self alloc] init];
    ao.bObjects = bObjects;
    return ao;
}
- (BObject*)firstBObject
{
    return [self.bObjects count] > 0 ? [self.bObjects objectAtIndex:0] : nil;
}
@end

BObject.h/m

@interface BObject : NSObject
+ (BObject*)bObjectWithName:(NSString*)name;
@property NSString *name;
@end

@implementation BObject
+ (BObject*)bObjectWithName:(NSString *)name
{
    BObject *bo = [[self alloc] init];
    bo.name = name;
    return bo;
}
@end

用法:

NSArray *aobjects = @[
                      [AObject aObjectWithBObjects:@[
                       [BObject bObjectWithName:@"A1B1"],
                       [BObject bObjectWithName:@"A1B2"],
                       [BObject bObjectWithName:@"A1B3"],
                       [BObject bObjectWithName:@"A1B4"]
                       ]],
                      [AObject aObjectWithBObjects:@[
                       [BObject bObjectWithName:@"A2B1"],
                       [BObject bObjectWithName:@"A2B2"],
                       [BObject bObjectWithName:@"A2B3"],
                       [BObject bObjectWithName:@"A2B4"]
                       ]],
                      [AObject aObjectWithBObjects:@[
                       [BObject bObjectWithName:@"A3B1"],
                       [BObject bObjectWithName:@"A3B2"],
                       [BObject bObjectWithName:@"A3B3"],
                       [BObject bObjectWithName:@"A3B4"]
                       ]]
                      ];
NSLog(@"%@", [aobjects valueForKeyPath:@"firstBObject.name"]);

结果

(
A1B1,
A2B1,
A3B1
)

【讨论】:

    【解决方案2】:

    事实证明,我很幸运能够在根类 (AObject) 中简单地覆盖 -valueForKey:。值得重申的是,-valueForKeyPath: 在每个键上都会调用 -valueForKey:,这很酷。

    由于这可能不适用于所有人,而且这可能过多地操纵默认的预期行为,因此这绝对不是“正确”答案。

    但无论如何它都在这里:

    - (id)valueForKey:(NSString *)string
    {
        if ([string characterAtIndex: [string length] - 1] == ']') // Trying to subscript
        {
            NSRegularExpression *subscriptRegex = [[NSRegularExpression alloc] initWithPattern: @"([a-zA-Z]+)\\[([0-9]+)\\]"
                                                                                       options: (NSRegularExpressionOptions)0
                                                                                         error: nil];
    
            NSString *key = [subscriptRegex stringByReplacingMatchesInString: string
                                                                     options: (NSMatchingOptions)0
                                                                       range: NSMakeRange(0, [string length])
                                                                withTemplate: @"$1"];
            id valueForKey = [self valueForKey: key];
            if (!key || !valueForKey || ![valueForKey respondsToSelector: @selector(objectAtIndexedSubscript:)])
                return nil;
    
            NSInteger index = [[subscriptRegex stringByReplacingMatchesInString: string
                                                                        options: (NSMatchingOptions)0
                                                                          range: NSMakeRange(0, [string length])
                                                                   withTemplate: @"$2"] integerValue];
            if ((index < 0) || (index >= [valueForKey count]))
                return nil;
    
            return [valueForKey objectAtIndexedSubscript: index];
        }
    
        return [super valueForKey: string];
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-06
      • 1970-01-01
      • 1970-01-01
      • 2012-04-04
      相关资源
      最近更新 更多